[fpc-pascal] best method: multiple three state options in a decision tree

Lukasz Sokol el.es.cr at gmail.com
Tue Jan 28 11:11:30 CET 2014


Hello Jürgen,



On 27/01/14 17:50, Jürgen Hestermann wrote:
[...] 
> 
> I recently had a similar problem and solved it this way:
> With
> 
> -----------------------------------
> type FlagType  = (FlagA,
>                   FlagB,
>                   FlagC,
>                   FlagD,
>                   FlagE,
>                   FlagF);
>      FlagSetType = set of FlagType;
> var  FlagSet : FlagSetType;
> -----------------------------------
> 
> I get a compact bit array (a set) where I can give each bit an individual name.
> Then these sets
> 
> -----------------------------------
> [FlagA,FlagB,FlagC,FlagD,FlagE,FlagF]
> [FlagA,      FlagC,FlagD,FlagE,FlagF]
> [FlagA,FlagB,FlagC,FlagD,FlagE,FlagF]
> [FlagA,            FlagD,FlagE,FlagF]
> [FlagA,FlagB,            FlagE,FlagF]
> [FlagA,FlagB,FlagC,FlagD,FlagE      ]
> -----------------------------------
> 
> select one "number" (which it internaly is) and
> 
> -----------------------------------
> byte([FlagA,FlagB,FlagC,FlagD,FlagE,FlagF])
> byte([FlagA,      FlagC,FlagD,FlagE,FlagF])
> byte([FlagA,FlagB,FlagC,FlagD,FlagE,FlagF])
> byte([FlagA,            FlagD,FlagE,FlagF])
> byte([FlagA,FlagB,            FlagE,FlagF])
> byte([FlagA,FlagB,FlagC,FlagD,FlagE      ])
> -----------------------------------

I thought about this too, but this means waldo's use case would be

type TOptions = [FlagAMin, FlagAMinMax, FlagAMax, ... FlagFMin, FlagFMinMax, FlagFMax] 

which would allow a nice usage of

if FlagAMin in Options then ...

but which he'd have to marshall (to disallow FlagA* set more than once) same way as he does now, bit by bit,
only more verbose... whereas the 'array of set' would be more readable [IMHO] and did that 'automatically'.

Since we need a way to group the flag values so that one flagset option can be one of 3 states 
[4 still be a better choice, being a power of 2 and uses same number of bits anyway ] 
and distinctly read/set the value of an option, my array idea was (I think) closest...

I also thought of

type TFlag = [FlagNone, FlagMin, FlagMinMax, FlagMax];

type TOptions = packed record
   FlagA : TFlag;
   FlagB : TFlag;
   ...
end;

but that is more awkward and ultimately the array is easier to expand.or to turn to dynamic.

A nice touch would be if

TFlag = [FlagNone, FlagMin, FlagMinMax, FlagMax];

TFlags = set of TFlag;

TOptions = set of TFlags; // which would still pack into available 'set' type of variable, 
// allowing constructs like byte(Options) to be used

but how would we then access individual options  in TOptions? AFAIK the above is impossible anyway;

Another I thought of was

type
TFlag = [FlagMin, FlagMinMax, FlagMax];

TFlagA = TFlag; TFlagB = TFlag; ... TFlagF = TFlag;

TOptions = [FlagA : TFlagA, ... FlagF : TFlagF]; 
// which ultimately is no different than the array approach
// but still less flexible
// and probaby impossible the way I wrote it above too.

Other ideas included having the flags use bitwise constants so that pushing/pulling it out of the set
would ensure the state of any Option be always of distinct choice, best if not 
having to think about clearing the remnants of other states by hand... but I failed to materialize this vision :)

The only way I could visualize was to have the distinct Options values declared as Flags shifted two bits to the left each,
and then yes using bitwise AND; this however limits number of options to bitsizeof(Byte) div 2 = 4 ... or bitsize(Word) div 2 = 8.
and requiring bitwise AND masks and then shift, for use with the rest of the Options value to read the value... messy.

Whereas with array of set this all happens beyond our influence...

All in all, IMHO and YMMV, again ;)

-L
> 
> can be used to convert them to real (integer) numbers in a case statement:
> 
> -----------------------------------
> case byte(FlagSet) of
>    byte([FlagA,FlagB,FlagC,FlagD,FlagE,FlagF]) : begin .. end;
>    byte([FlagA,      FlagC,FlagD,FlagE,FlagF]) : begin .. end;
>    byte([FlagA,FlagB,FlagC,FlagD,FlagE,FlagF]) : begin .. end;
>    byte([FlagA,            FlagD,FlagE,FlagF]) : begin .. end;
>    byte([FlagA,FlagB,            FlagE,FlagF]) : begin .. end;
>    byte([FlagA,FlagB,FlagC,FlagD,FlagE      ]) : begin .. end;
>    end; // of case
> -----------------------------------
> 
> and still I can use the set as a usual set:
> 
> -----------------------------------
> include(FlagSet,FlagD);
> ...
> if FlagA in Flagset then..
> ...
> etc.
> -----------------------------------
> 
> I even use an array which is indexed with the set variable:
> 
> -----------------------------------
> type FlagSetArrayType = array[0..(1 shl (ord(High(FlagSetType))+1))-1] of any_other_type; // 0..2^n-1
> -----------------------------------
> 
> which holds information for all 2^n possible flag combination in the set.
> Then I can use the set variable to index the array like this:
> 
> -----------------------------------
> var FlagSetArray = FlagSetArrayType;
> ...
> 
> any_other_type_variable := FlagSetArray[ byte([FlagA,FlagB,FlagC,FlagD,FlagE,FlagF])   ];
> -----------------------------------
> 
> (I hope there is no typo in the above code)





More information about the fpc-pascal mailing list