什么 Delphi 类型的'整数集'?

vmdwslir  于 2022-11-23  发布在  其他
关注(0)|答案(5)|浏览(192)

我有几个硬编码的验证,如下所示:

const
  cLstAct      =  1;   
  cLstOrg      =  4;
  cLstClockAct = 11;
const
  FUNCT_1 = 224;
  FUNCT_2 = 127;
  FUNCT_3 =   3;

if lFuncID in [FUNCT_1,FUNCT_2,FUNCT_3] then ...
if not (lListType in [cLstAct..cLstOrg,cLstClockAct]) then ...
if not (lPurpose in [0..2]) then ...

我想用一个通用的方法来代替

function ValidateInSet(AIntValue: integer; AIntSet: @@@): Boolean;
begin
  Result := (AIntValue in AIntSet);
  if not Result then ...
end;

但是对于AIntSet,应该选择什么类型呢?
目前,整个代码中要测试的值达到了常量值232(因此,我可以使用TByteSet = Set of Byte),但我可以预见,当常量值超过255时,我们将遇到E1012 Constant expression violates subrange bounds
我的谷歌搜索失败了。
(目前在 Delphi 西雅图更新1)

0yg35tkg

0yg35tkg1#

使用字典TDictionary<Integer, Integer>。值是无关紧要的,你只关心键。如果字典包含一个特定的键,那么这个键就是集合的成员。使用AddOrSetValue添加成员,使用Remove删除成员,使用ContainsKey测试成员资格。
使用字典的意义在于它提供了O(1)查找。
您不希望直接将此类型用作集。您应该将其 Package 在一个类中,该类只公开类似于集的功能。可以在此处找到这样的示例:https://stackoverflow.com/a/33530037/505088

h5qlskok

h5qlskok2#

您可以使用array of Integer

function ValidateInSet(AIntValue: integer; AIntSet: array of Integer): Boolean;
var
  I: Integer;
begin
  Result := False;
  for I := Low(AIntSet) to High(AIntSet) do
  begin
    if AIntSet[I] = AIntValue then
    begin
      Result := True;
      Break;
    end;
  end;
  if not Result then ...
end;
const
  cLstAct      =  1;   
  cLstOrg      =  4;
  cLstClockAct = 11;
const
  FUNCT_1 = 224;
  FUNCT_2 = 127;
  FUNCT_3 =   3;

if ValidateInSet(lFuncID, [FUNCT_1, FUNCT_2, FUNCT_3]) then ...
if not ValidateInSet(lListType, [cLstAct, 2, 3, cLstOrg, cLstClockAct]) then ...
if not ValidateInSet(lPurpose, [0, 1, 2]) then ...
jdzmm42g

jdzmm42g3#

如果您使用的是最近的 Delphi 版本,则可以使用TArray<Integer>

function ValidateInSet(AIntValue: integer; const AIntSet: TArray<Integer>): Boolean;
var
  N: Integer;
begin
  { option1 : if AIntSet is always sorted }
  result := TArray.BinarySearch(AIntSet, AIntValue, N);

  { option 2: works for any array }
  result := false;
  for N in AIntSet do begin
    if AIntValue = N then begin
      result := true;
      Break;
    end;
  end;

  if not Result then begin
    // ...
  end;
end;

调用仅仅与使用集合相同(范围除外):

if ValidateInSet(lFuncID, [FUNCT_1,FUNCT_2,FUNCT_3]) then begin

  end;
wbgh16ku

wbgh16ku4#

直接答案是TBits
http://docwiki.embarcadero.com/Libraries/Seattle/en/System.Classes.TBits.Bits
注意:这只能从 Delphi XE 4开始使用,但必须使用-http://qc.embarcadero.com/wc/qcmain.aspx?d=108829
然而,对于你的“整数集”,在大多数膨胀的情况下,它将占用2^31 / 8字节的内存(因为整数的负值甚至不会被考虑),这将是很多...所以我希望你永远不会真的想要一个完整整数的集合。或者你应该投资稀疏数组代替。

function ValidateInSet(const AIntValue: integer; const AIntSet: TBits): Boolean;
begin
  Result := (AIntValue >= 0) and (AIntValue < AIntSet.Size);
  if Result then
     Result := AIntSet.Bits[AIntValue];
  if not Result then ...
     v-a-l-i-d-a-t-e
end;

或者更确切地说

function ValidateInSet(const AIntValue: integer; const AIntSet: TBits): Boolean;
begin
  Result := false;

  if AIntValue < 0 then exit;               // Validation criterion #1
  if AIntValue >= AIntSet.Size then exit;   // Validation criterion #2
  if not AIntSet.Bits[AIntValue] then exit; // Validation criterion #3

  if .... then exit;                        // Validation criterion #4
  if .... then exit;                        // Validation criterion #5
  if .... then exit;                        // Validation criterion #6

  Result := true;
end;

或者也许

TSetTestCriterion = TFunc<Integer, Boolean>;
TSetTestCriteria  = TArray<TFunc<Integer, Boolean>>;

function ValidateInSet(const AIntValue: integer; 
     const AIntSet: TBits; const Tests: TSetTestCriteria = nil): Boolean;
var ExtraTest: TSetTestCriterion;
begin
  Result := false;

  if AIntValue < 0 then exit;               // Validation criterion #1
  if AIntValue >= AIntSet.Size then exit;   // Validation criterion #2
  if not AIntSet.Bits[AIntValue] then exit; // Validation criterion #3

  if Tests <> nil then           // Validation criteria #4, #5, #6, ...
    for ExtraTest in Tests do
      if not ExtraTest(AIntValue) then exit;        

  Result := true;
end;

http://docwiki.embarcadero.com/Libraries/Seattle/en/System.SysUtils.TFunc
现在-只是为了演示,在真实的的应用程序中,您将创建这些集和数组一次,并缓存很长时间(永远,或至少除非配置更改需要重建它们)。

Type FuncIDs = ( FUNCT_3 =   3, FUNCT_2 = 127, FUNCT_1 = 224);

var MysticGlobalFlag: Boolean;

function ValidateFuncID( const lFuncID: FuncIDs): Boolean;
var map: TBits;
begin
  map := TBits.Create;
  try
    map.Size := High(lFuncID) + 1;
    map.Bits[ Ord(Func_1) ] := True;
    map.Bits[ Ord(Func_2) ] := True;
    map.Bits[ Ord(Func_3) ] := True;

    Result := ValidateInSet( Ord(lFuncID), map,
       TSetTestCriteria.Create(
          function( lFuncID: integer) : Boolean
          begin
            Result := MysticGlobalFlag or (lFuncID <> Ord(FuncIDs.FUNC_2))
          end
       , 
          function( lFuncID: integer) : Boolean
          begin
            Result := (lFuncID <> Ord(FuncIDs.FUNC_3)) or (DayOfTheWeek(Now()) = 4)
          end
       )
    );
  finally
    map.Destroy;
  end;

  if not Result then  // from the original question code
     ...              // seems like a placeholder for error handling or object creation and registration
end;
ekqde3dh

ekqde3dh5#

总之,我知道人们已经很多年没有回答这个问题了,但是这里有一个使用 Delphi 泛型的新解决方案:-

interface
uses
  System.Generics.Defaults;

type
  TUtilityArray<T> = class
  public
    class function Contains(const x : T; const an_array : array of T) : boolean;
  end;

implementation

class function TUtilityArray<T>.Contains(const x: T; const an_array: array of T): boolean;
var
  y          : T;
  l_comparer : IEqualityComparer<T>;
begin
  Result     := false;
  l_comparer := TEqualityComparer<T>.Default;
  for y in an_array do
  begin
    if l_comparer.Equals(x, y) then
    begin
      Result := true;
      break;
    end;
  end;
end;


end.

要使用include类,则编写if(TUtilityArray<integer>.Contains(some integer value, [value1, value2 etc.])) then ...。此方法的另一个好处是它也适用于其他原语。

相关问题