AFAIK dafür gibt es keine eingebaute Funktion. Beim Durchsuchen des Webs habe ich diese Funktion gefunden und sie funktioniert für mich. Aber ich bevorzuge es nicht zu verwenden, da es zusammengebaut ist und ich nicht verstehen kann, was es tut. Also habe ich diese Funktion geschrieben, die auch funktioniert:

function Cardinality(const PSet: PByteArray;
  const SizeOfSet(*in bytes*): Integer): Integer;
const
  Masks: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
var
  I, J: Integer;
begin
  Result := 0;
  for I := 0 to SizeOfSet - 1 do
    for J := 0 to 7 do
      if (PSet^[I] and Masks[J]) > 0 then
        Inc(Result);
end;

Jetzt möchte ich wissen, ob ich mich auf diese Funktion verlassen kann? Oder vielleicht steckt hinter dem festgelegten Datentyp ein Trick, weshalb delphi keine integrierte Methode dafür hat.

Aber if meine Funktion ist zuverlässig then wie kann ich sie verbessern auf:

  1. Übergeben Sie Konstanten
  2. Führen Sie eine Typprüfung durch und stellen Sie sicher, dass ein Satz an die Funktion übergeben wird
  3. Übergeben Sie den Wert anstelle seiner Adresse
  4. Entfernen Sie den Parameter SizeOfSet

Ich möchte es wie Cardinality(AnySet) anstelle von Cardinality(@AnySet, SizeOf(TAnySet)) nennen.

Übrigens muss ich das sowohl in XE als auch in XE5 kompilieren.

10
saastn 23 Dez. 2015 im 22:02

2 Antworten

Beste Antwort

Sie können dies mit Generika und RTTI implementieren. Wie so:

uses
  SysUtils, TypInfo;

type
  ERuntimeTypeError = class(Exception);

  TSet<T> = class
  strict private
    class function TypeInfo: PTypeInfo; inline; static;
  public
    class function IsSet: Boolean; static;
    class function Cardinality(const Value: T): Integer; static;
  end;

const
  Masks: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);

implementation

{ TSet<T> }

class function TSet<T>.TypeInfo: PTypeInfo;
begin
  Result := System.TypeInfo(T);
end;

class function TSet<T>.IsSet: Boolean;
begin
  Result := TypeInfo.Kind=tkSet;
end;

function GetCardinality(const PSet: PByteArray;
  const SizeOfSet(*in bytes*): Integer): Integer; inline;
var
  I, J: Integer;
begin
  Result := 0;
  for I := 0 to SizeOfSet - 1 do
    for J := 0 to 7 do
      if (PSet^[I] and Masks[J]) > 0 then
        Inc(Result);
end;

class function TSet<T>.Cardinality(const Value: T): Integer;
var
  EnumTypeData: PTypeData;
begin
  if not IsSet then
    raise ERuntimeTypeError.Create('Invalid type in TSet<T>, T must be a set');
  Result := GetCardinality(PByteArray(@Value), SizeOf(Value));
end;

Verwendung:

Writeln(TSet<SomeSet>.Cardinality(Value));
8
saastn 24 Dez. 2015 im 14:56

In früheren Versionen von Delphi können Sie dies tun:

function Card(ASet: TSet): Integer;
var
  k: TSetElement;
begin
  Result := 0;
  for k in ASet do Inc(Result);
end;
4
Anonymous 14 Nov. 2017 im 14:33