2010-11-04 1 views
6

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

ответ

4

Попробуйте

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

Это действительно работает. Я предполагаю, что это не сработает для наборов с более чем 8 участниками? (хотя к этому моменту алгоритм, вероятно, уже слишком медленный). –

+1

Сделайте sizeof (SetVar), чтобы узнать, как определенный набор представлен Delphi. –

2

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

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

Для более чем 16, но менее 32 переменная DWord и тип.

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

+1

Проблема не в размере целого числа, проблема в том, что он пытается применить к типу * set * (TMyOptions), а не к перечисляемому типу. –

+0

Альберто: В этом случае, конечно, возможно, что набор в байтовом слове-слововом слове. – jachguate

0

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

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

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

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

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

+0

См. Принятый ответ. Использование байта, а не целочисленного, работает. –

+1

erm ... Вы можете перечислить содержимое набора. Это просто противоречиво. то есть для I: = Low (Enum) до high (Enum) do ///, если I в Set then. –

+2

Или, как из Delphi 2005, вы можете напрямую перечислять содержимое: 'для el in s do ...' –

0

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

Отливки из Integer в набор не представляется возможным, но Tondrej однажды написал blog article на 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; 

--jeroen

6

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

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; 
+0

Очень приятно, что он выглядит намного опрятным (хотя я могу придерживаться другого только потому, что он быстрее). –