2010-11-04 21 views
6

Tengo un algoritmo de cálculo en Delphi con varias opciones diferentes, y tengo que probar cada combinación de opciones para encontrar una solución óptima.Enumerar posibles valores establecidos en Delphi

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

me preguntaba sobre el uso de un bucle de entero a enumerarlos:

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

Esto no compila. Lo que me preguntaba era si había algún método bastante simple para convertir de Integer a Set (la mayoría de las preguntas en la Web intentan ir para el otro lado, de Set a Integer), y si es así, ¿qué es?

Otra posibilidad es usar el entero como un campo de bits:

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

y luego membresía de prueba con un nivel de bits y:

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

He intentado esto, y funciona , pero parece que trabajar con conjuntos sería más natural y usar mejor el sistema de tipos (aunque salgo fuera del sistema de tipo mencionado para enumerar los conjuntos).

¿Existe una manera mejor/más segura de enumerar todas las combinaciones de conjuntos posibles que enumerar la representación entera subyacente?

Notas:

  1. sé que los valores enteros de un conjunto no están garantizados en la teoría (aunque sospecho que son en la práctica si no se juega con la numeración de enumeración).
  2. Podría haber más de cuatro opciones (sí, sé que crece exponencialmente y si hay demasiadas opciones, el algoritmo podría tardar una eternidad).

Respuesta

4

Trate

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

que en realidad funciona. ¿Asumo que no funcionaría para conjuntos con más de 8 miembros? (aunque en ese momento el algoritmo probablemente ya sea demasiado lento). –

+1

Haz sizeof (SetVar) para descubrir cómo un conjunto particular está representado por Delphi. –

2

Su código no se compila porque su enumeración (TMyOption) tiene menos de 8 valores, y Delphi utilizan el tamaño mínimo posible (en bytes) para los conjuntos. Por lo tanto, una variable byte funcionará para usted.

Si tiene un conjunto con más de 8 pero menos de 16 elementos posibles, una palabra funcionará (y no un número entero).

Para más de 16 pero menos de 32 una variable DWord y tipocast.

Para más de 32 elementos posibles, creo que un mejor enfoque es usar una matriz de bytes o algo así.

+1

El problema no es el tamaño del número entero, el problema es que está intentando convertir al tipo * set * (TMyOptions), no al tipo enumerado. –

+0

Alberto: El conjunto de tipos de byte-palabra-dword es por supuesto posible y significativo en este caso. – jachguate

0

El problema es que está intentando convertir al tipo de conjunto en lugar del tipo enumerado. Puede convertir entre enteros y enumerados porque ambos son tipos ordinales, pero no se puede convertir a un conjunto porque usan bitfiels como ya ha señalado. Si usa:

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

funcionaría, aunque no es lo que desea.

Tuve este mismo problema hace unos meses y llegué a la conclusión de que no se puede enumerar el contenido de un conjunto en Delphi (al menos en Delphi 7) porque el lenguaje no define dicha operación en un conjunto .

Editar: Parece que puedes incluso en D7, mira los comentarios a esta respuesta.

+0

Ver la respuesta aceptada. Usar un byte en lugar de un entero funciona. –

+1

erm ... PUEDE enumerar el contenido de un conjunto. Es contra-intuitivo. es decir, para I: = bajo (Enum) a alto (Enum) do /// si en Set entonces. –

+2

O, a partir de Delphi 2005, puede enumerar directamente los contenidos: 'for the in s do ...' –

0

500 - Servidor interno La respuesta del error es probablemente la más simple.

Otro enfoque que sería menos probable que rompa con los cambios en el número de opciones sería declarar una matriz de booleanos y activarlos/desactivarlos. Sin embargo, esto es más lento que trabajar con enteros puros. La principal ventaja es que no necesitará cambiar el tipo de entero que usa, y puede usarlo si tiene más de 32 opciones.

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

Colada de un entero a un conjunto no es posible, pero Tondrej escribió una vez un blog article en SetToString y StringToSet que expone lo que quiere en la SetOrdValue método:

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; 

su código a continuación, se convertiría esto:

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

--jeroen

6

Sé que esta pregunta es bastante antiguo, pero esta es mi preferencia, ya que es simple y natural para mí:

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

Muy bonito, lo hace parecer mucho más ordenado (aunque podría quedarme con el otro simplemente porque es más rápido). –

Cuestiones relacionadas