Перечисление возможных значений набора в Delphi
у меня есть алгоритм расчета в 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), и если да, то что это?
другая возможность-просто использовать целое число в качестве битового поля:
C_Option1 = 1;
C_Option2 = 2;
C_Option3 = 4;
C_Option4 = 8;
а затем проверить членство с помощью побитового и:
if (Options and C_Option2) > 0 then begin
...
end;
Я пробовал это, и это работает, но похоже, что работа с наборами будет более естественной и лучше использовать систему типов (хотя я выхожу за пределы указанной системы типов для перечисления наборов).
есть ли лучший / более безопасный способ перечислить все возможные комбинации наборов, чем перечисление базового целочисленное представление?
Примечания:
- я знаю, что целочисленные значения множества не гарантированы в теории (хотя я подозреваю, что они на практике, если вы не играете с нумерацией перечислений).
- может быть более четырех вариантов (да, я знаю, что он растет экспоненциально, и если слишком много вариантов, алгоритм может занять вечность).
6 ответов
попробовать
var EnumerationByte: Byte;
...
for EnumerationByte := 0 to 15 do begin
Options := TMyOptions(EnumerationByte);
end;
Я знаю, что этот вопрос довольно старый, но это мое предпочтение, так как это просто и естественно для меня :
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;
ваш код не компилируется, потому что ваше перечисление (TMyOption) имеет менее 8 значений, а Delphi использует минимально возможный размер (в байтах) для наборов. Таким образом, переменная byte будет работать для вас.
Если у вас есть набор с более чем 8, но менее чем 16 возможных элементов, слово будет работать (а не целое число).
для более чем 16, но менее чем 32 переменной DWord и typecast.
для более чем 32 возможных элементов, я думаю, что лучший подход использовать массив байтов или что-то в этом роде.
500-ответ внутренней ошибки сервера, вероятно, самый простой.
другой подход, который с меньшей вероятностью нарушит изменения количества опций, будет заключаться в объявлении массива boolean и включении/выключении их. Это медленнее, чем работа с чистыми целыми числами. Главное преимущество, вам не нужно будет изменять целочисленный тип, который вы используете, и вы можете использовать его, если у вас есть более 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;
кастинг из целого числа в множество невозможен, но Tondrej как пишет статья в блоге on 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
проблема в том, что вы пытаетесь привести к типу set вместо перечисляемого типа. Вы можете выполнять приведение между integer и enumerated, поскольку оба типа являются порядковыми, но вы не можете выполнить приведение к набору, поскольку они используют bitfiels, как вы уже отметили. Если вы используете:
for EnumerationInteger := 0 to 15 do begin
Option := TMyOption(EnumerationInteger);
end;
это будет работать, хотя это не то, что вы хотите.
У меня была эта же проблема несколько месяцев назад и пришел к выводу, что вы не можете перечислить содержимое набора в Delphi (по крайней мере в Delphi 7), потому что язык не определяет такую операцию на множестве.
редактировать: кажется, что вы можете даже в D7, см. coments к этому ответу.