Перечислите возможные заданные значения в Delphi - PullRequest
6 голосов
/ 04 ноября 2010

У меня есть алгоритм расчета в Delphi с несколькими различными опциями, и мне нужно попробовать каждую комбинацию опций, чтобы найти оптимальное решение.

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

Я задавался вопросом об использовании целочисленного цикла для их перечисления:

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

Это не компилируется. Мне было интересно, был ли какой-нибудь довольно простой метод преобразования из Integer в Set (большинство вопросов в Интернете пытаются пойти другим путем, от 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. Может быть более четырех вариантов (да, я знаю, что он растет в геометрической прогрессии, и если вариантов слишком много, алгоритм может работать вечно).

Ответы [ 6 ]

6 голосов
/ 08 ноября 2010

Я знаю, что этот вопрос довольно старый, но это мое предпочтение, так как для меня это просто и естественно:

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;
4 голосов
/ 04 ноября 2010

Попробуйте

var EnumerationByte: Byte;
...
for EnumerationByte := 0 to 15 do begin
    Options := TMyOptions(EnumerationByte);
end;
2 голосов
/ 04 ноября 2010

Ваш код не компилируется, потому что ваше перечисление (TMyOption) имеет менее 8 значений, а Delphi использует минимально возможный размер (в байтах) для наборов.Таким образом, переменная байта будет работать для вас.

Если у вас есть набор с более чем 8, но менее чем 16 возможными элементами, Word будет работать (а не целое число).больше 16, но меньше 32 переменная DWord и typecast.

Для более чем 32 возможных элементов, я думаю, что лучше использовать массив байтов или что-то в этом роде.

0 голосов
/ 04 ноября 2010

Преобразование из целого числа в набор невозможно, но Tondrej однажды написал статью блога на SetToString и StringToSet, которая раскрывает то, что вы хотите в 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;

- Йерун

0 голосов
/ 04 ноября 2010

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;
0 голосов
/ 04 ноября 2010

Проблема в том, что вы пытаетесь привести к типу набора вместо типа перечисления.Вы можете приводить между integer и enumerated, потому что оба являются порядковыми типами, но вы не можете приводить к набору, потому что они используют битовые поля, как вы уже заметили.Если вы используете:

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

, это будет работать, хотя это не то, что вы хотите.

У меня была такая же проблема несколько месяцев назад, и я пришел к выводу, что вы не можете перечислитьсодержимое набора в Delphi (по крайней мере, в Delphi 7), потому что язык не определяет такую ​​операцию над набором.

Edit : кажется, что вы можете даже в D7,см. комментарии к этому ответу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...