2016-04-22 64 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/)。 –