How to compare sets of enumerated types

2020-07-07 06:38发布

问题:

From a certain point I got tired of writing set conditions (and, or), because for more conditions or longer variable names it begins to be clumsy and annoying to write all over again. So I started writing helpers so I could write ASet.ContainsOne([ceValue1, ceValue2]) instead of (ceValue1 in ASet) or (ceValue2 in ASet).

type
  TCustomEnum = (ceValue1, ceValue2, ceValue3);
  TCustomSet = set of TCustomEnum;
  TCustomSetHelper = record helper for TCustomSet 
    function ContainsOne(ASet: TCustomSet): Boolean;
    function ContainsAll(ASet: TCustomSet): Boolean;
  end;

implementation

function TCustomSetHelper.ContainsOne(ASet: TCustomSet): Boolean;
var
  lValue : TCustomEnum;
begin
  for lValue in ASet do
  begin
    if lValue in Self then
      Exit(True);
  end;
  Result := False;
end;

function TCustomSetHelper.ContainsAll(ASet: TCustomSet): Boolean;
var
  lValue : TCustomEnum;
begin
  Result := True;
  for lValue in ASet do
  begin
    if not (lValue in Self) then
      Exit(False);
  end;
end;

Unfortunately, this is not the most effective solution and it's against the DRY principle. To my surprise, I didn't find anyone ever dealing with the same problem, so I wonder if there is any better (generic) solution?

回答1:

The set operators help you implement these functions

For ContainsOne we use the * operator which is the set intersection operator.

function TCustomSetHelper.ContainsOne(ASet: TCustomSet): Boolean;
begin
  Result := ASet * Self <> [];
end;

For ContainsAll we would use <= which is the subset operator.

function TCustomSetHelper.ContainsAll(ASet: TCustomSet): Boolean;
begin
  Result := ASet <= Self;
end;

Given how simple these expressions are, I question whether or not you need the helper type at all.

The documentation gives the full list of available set operators.



回答2:

You can use the set intersection operator

For ContainsOne analog check if intersection is not empty set, for ContainsAll check that intersection coincides with argument set

type
  TCustomEnum = (ceValue1, ceValue2, ceValue3);
  TCustomSet = set of TCustomEnum;
var
  ASet: TCustomSet;
begin
  ASet := [ceValue1, ceValue3];

  if ([ceValue1, ceValue2] *  ASet) <> [] then
     Memo1.Lines.Add('Somebody here');

  if ([ceValue1, ceValue3] *  ASet) = [ceValue1, ceValue3] then
     Memo1.Lines.Add('All are in home');