2016-04-22 111 views
9

我有几个硬编码验证这样的:德尔福为'整数集'类型?

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 =字节集),但是我可以预见,当常数值超过时,我们会碰到E1012 Constant expression violates subrange bounds 255.

我的谷歌福失败,在这里我...

(目前德尔福西雅图更新1)

+1

Spring4D有THashSet:http://www.devjetsoftware.com/docs/spring4d/index.htm?Spring .Collections.Sets.THashSet%28T%29.IsSupersetOf.htm – Johan

+0

有趣的是,FPC中的AFAIR可以创建与内存将包含的大集合。虽然我不确定这个特定的Delphi限制是不是真的有优势... –

+0

您可以使用泛型集[here](http://stackoverflow.com/a/19524788/576719)。 –

回答

4

您可以使用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 ... 
+0

我正在寻找这个解决方案,因为它最适合我现有的代码;不需要预先定义和填充结构(就像我必须使用TDictionary一样)。 我必须改变的唯一事情是例如如果不是ValidateInSet(lPurpose,[0,1,2],'purpose',false),则返回'如果不是ValidateInSet(lPurpose,[0..2],'purpose',false) –

8

使用一本字典,TDictionary<Integer, Integer>。价值无关紧要,你只关心关键。如果字典包含特定的密钥,那么该密钥是该组的成员。使用AddOrSetValue添加成员,Remove删除成员,并使用ContainsKey来测试成员资格。

使用字典的一点是它给你O(1)查找。

你不想直接使用这种类型作为一个集合。你应该把它包装在一个刚刚公开设置类似功能的类中。一个例子可以在这里找到:https://stackoverflow.com/a/33530037/505088

+1

'TDictionary '会更好 –

+0

@arioch不是真的没有 –

+0

为什么?对于那些可以减少占用空间的整数。 –

1

如果你在最近的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; 
+2

我会强烈地强调O(1)访问很重要,并且由于它很容易实现,所以对O(n)访问感觉很差。 –

+0

试过这个,但不起作用:你的示例语句中的不兼容类型,更不用说像'ValidateInSet(lListType,[cLstAct..cLstOrg,cLstClockAct])' –

+0

由于设计相当薄弱,数组和集合之间存在很多语法混淆数组文字。 –

1

直接的答案是TBits

http://docwiki.embarcadero.com/Libraries/Seattle/en/System.Classes.TBits.Bits

注:此只能使用从德尔福XE4开始 - 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; 
+1

我不明白为什么你的函数检查一个值是否在一个集合中是如此复杂。 –

+0

因为主题启动器在搜索失败后添加了特殊处理。无论如何,删除额外的测试是一件轻而易举的事。然而,TS想要在不同的域中使用相同的功能,然后扩展性是一件好事 –

+1

请注意[QualityCentral现在已关闭](https://community.embarcadero.com/blogs/entry/quality-keeps-moving -forward),所以你不能访问'qc.embarcadero.com'链接了。如果您需要访问旧的QC数据,请查看[QCScraper](http://www.uweraabe.de/Blog/2017/06/09/how-to-save-qualitycentral/)。 –