簡體   English   中英

在Delphi中枚舉可能的設置值

[英]Enumerate possible set values in Delphi

我在Delphi中有一個計算算法,有許多不同的選項,我需要嘗試每個選項組合來找到最佳解決方案。

TMyOption = (option1, option2, option3, option4);
TMyOptions = set of TMyOption;

我想知道使用Integer循環枚舉它們:

for EnumerationInteger := 0 to 15 do begin
    Options := TMyOptions(EnumerationInteger);
end;

這不編譯。 我想知道的是,是否有任何相當簡單的方法可以從Integer轉換為Set(Web上的大多數問題都試圖從另一個方向轉換,從Set到Integer),如果是這樣,它是什么?

另一種可能性是將Integer用作位域:

C_Option1 = 1;
C_Option2 = 2;
C_Option3 = 4;
C_Option4 = 8;

然后按位測試成員資格:

if (Options and C_Option2) > 0 then begin
    ...
end;

我已經嘗試了這個,並且它可以工作,但感覺就像使用集合會更自然並且更好地使用類型系統(即使我要在所述類型系統外部枚舉集合)。

有沒有比枚舉基礎整數表示更好/更安全地枚舉所有可能的集合組合?

筆記:

  1. 我知道理論上不保證集合的整數值(盡管如果你不使用枚舉編號,我懷疑它們在實踐中)。
  2. 可能有四個以上的選項(是的,我知道它會以指數方式增長,如果有太多選項,算法可能會永遠占用)。

我知道這個問題已經很老了,但這是我的偏好,因為它對我來說簡單而自然:

function NumericToMyOptions(n: integer): TMyOptions;
var
  Op: TMyOption;
begin
  Result:= [];
  for Op:= Low(TMyOption) to High(TMyOption) do
    if n and (1 shl ord(Op)) > 0 then Include(Result, Op);
end;

嘗試

var EnumerationByte: Byte;
...
for EnumerationByte := 0 to 15 do begin
    Options := TMyOptions(EnumerationByte);
end;

您的代碼無法編譯,因為您的枚舉(TMyOption)的值小於8,Delphi使用最小可能的大小(以字節為單位)。 因此,字節變量將適合您。

如果你有一個超過8但少於16個可能元素的集合,Word將起作用(而不是整數)。

對於超過16但少於32的DWord變量和類型轉換。

對於超過32個可能的元素,我認為更好的方法是使用字節數組或類似的東西。

問題是您正在嘗試轉換為set類型而不是枚舉類型。 您可以在整數和枚舉之間進行轉換,因為它們都是序數類型,但您不能轉換為集合,因為它們使用了您已經注意到的bitfiels。 如果您使用:

for EnumerationInteger := 0 to 15 do begin
  Option := TMyOption(EnumerationInteger);
end;

它會工作,雖然不是你想要的。

幾個月前我遇到了同樣的問題,得出的結論是你無法枚舉Delphi中的一個集合的內容(至少在Delphi 7中),因為語言沒有在集合上定義這樣的操作。

編輯 :看來你甚至可以在D7中看到這個答案的咒語。

500 - 內部服務器錯誤的答案可能是最簡單的。

另一種不太可能隨着選項數量的變化而中斷的方法是聲明一個布爾數組,並打開/關閉它們。 這比使用純整數要慢。 主要優點是,您不需要更改您使用的整數類型,如果您有超過32個選項,則可以使用它。

procedure DoSomething
var BoolFlags : Array[TOption] of Boolean;
    I: TOption;
  function GetNextFlagSet(var Bools : Array of Boolean) : Boolean;
  var idx, I : Integer;
  begin
    idx := 0;
    while Bools[idx] and (idx <= High(Bools)) do Inc(idx);

    Result := idx <= High(Bools);

    if Result then
      for I := 0 to idx do
        Bools[I] := not Bools[I];
  end;
begin
  for I := Low(BoolFlags) to High(BoolFlags) do BoolFlags[i] := False;

  repeat
    if BoolFlags[Option1] then
      [...]

  until not GetNextFlagSet(BoolFlags);
end;

從一個Integer到一個Set的轉換是不可能的,但是Tondrej曾經寫過一篇關於SetToStringStringToSet博客文章SetToStringSetOrdValue方法中公開了你想要的SetOrdValue

uses
  TypInfo;

procedure SetOrdValue(Info: PTypeInfo; var SetParam; Value: Integer);
begin
  case GetTypeData(Info)^.OrdType of
    otSByte, otUByte:
      Byte(SetParam) := Value;
    otSWord, otUWord:
      Word(SetParam) := Value;
    otSLong, otULong:
      Integer(SetParam) := Value;
  end;
end;

您的代碼將成為這樣:

for EnumerationInteger := 0 to 15 do begin
    SetOrdValue(TypeInfo(TMyOptions), Options, EnumerationInteger);
end;

--jeroen

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM