Einzelnen Beitrag anzeigen

Taweluki

Registriert seit: 26. Nov 2004
2 Beiträge
 
#9

Re: Mengenoperatoren - ein kleiner Kurs

  Alt 8. Jan 2005, 15:40
Zitat von Dax:
... der Differenzierungsoperator der Sets und XOR sind de facto das selbe. Es werden die Elemente geliefert, die nur eins der Sets enthält, die anderen werden ausgelassen, wie bei XOR.
Wie andere bereits zeigten, ist dem nicht so. Trotzdem, manchmal braucht man auch XOR für Mengen. Leider ist die Mengenunterstützung in Delphi/Object Pascal etwas dürftig ausgefallen (gilt zumindest bis einschließlich D7). So besitzt Delphi - im Gegensatz zu manch anderer Sprache - für die beiden folgenden Mengenoperationen keine eingebauten Operatoren. Wir können sie aber mit Hilfe der vorhandenen Operatoren nachbilden.

Symmetrischen Mengendifferenz
Die symmetrische Mengendifferenz ist das XOR der Sets.
Form: (Set1 - Set2) + (Set2 - Set1): Set
Alternativ: (Set1 + Set2) - (Set1 * Set2): Set
Das Ergebnis enthält all die Elemente, die zu Set1 oder Set2, jedoch nicht zu beiden gehören.

Delphi-Quellcode:
(X - Y) + (Y - X) = [6,7]
(A - B) + (B - A) = [1..6]
(A - Y) + (Y - A) = [1,3,5]
(B - Y) + (Y - B) = [2,4,6]
(A - X) + (X - A) = [1,3,5,6,7]
(B - X) + (X - B) = [2,4,7]
Eine typische Anwendung ist z.B. die Folgende:

Delphi-Quellcode:
type
  TMyOption = (opt1, opt2, opt3, opt4, opt5);
  TMyOptions = set of TMyOption;

  TMyClass = class(TWinControl)
  ...

procedure TMyClass.SetOptions(const Value: TMyOptions);
var
  ChangedOptions: TMyOptions;
const
  NeedRecreate = [opt1, opt3];
  NeedRepaint = [opt4];
begin
  ChangedOptions := (Value - FOptions) + (FOptions - Value);
  if ChangedOptions <> [] then
  begin
    FOptions := Value;
    if HandleAllocated then
// bitte, liebe Komponentenersteller: diese Abfrage nie vergessen! Wer nicht weiß, was ich
// meine, der leite mal eine Komponente von TRichEdit ab, die nichts anderes tut, als einen
// neuen Vorgabewert für die Hintergrundfarbe zu implementieren. Bringt zumindest bis zur
// ungepatchten D7 echt Laune. ;-)
      if (NeedRecreate * ChangedOptions) <> [] then
        RecreateWnd
      else if (NeedRepaint * ChangedOptions) <> [] then
        Repaint;
  end;
end;

Komplementärmengen
Das NOT der Sets. Wieder besitzt Delphi keinen eigenen Operator, denn der Mengendifferenz-Operator "-" ist - anders als der normale Differenzoperator - nicht unär. Zum Nachbilden benötigen wir eine Hilfskonstante, die Menge aller möglichen Elemente: const AllElements{: Mengentyp} = [minElement..maxElement]; Leider benötigen wir derartige Konstanten für jeden einzelnen Mengentyp.
Form: AllElements - Set1: Set
Das Ergebnis enthält alle Elemente des Basistyps, die nicht in Set1 enthalten sind.
Delphi-Quellcode:
AllElements: set of Byte = [1..7] // erscheint sinnvoll für die gegebenen A, B, X und Y
AllElements - X = [1..5,7]
AllElements - Y = [1..6]
AllElements - A = [2,4,6]
AllElements - B = [1,3,5]
Nur mal so am Rande: Das Beispiel für das Mengen-XOR kommt nicht von ungefähr. Vielleicht kennt jemand von euch ja die VirtualShellTools (eine Erweiterung der open source VirtualTree Komponenten). Schaut euch mal die Set..Options Prozeduren im Quelltext von VirtualExplorerTree an. Da war jemand am Werk, der mit Mengenoperatoren deutlich auf Kriegsfuß steht (im Gegensatz zu den VirtualTree Programmierern, die haben das vorbildlich gelöst). Ist aber nur ein kleiner Schönheitsfehler, die Komponenten sind ansonsten ziemlich genial. Zeigt aber, dass ein Kurs wie dieser sinnvoll ist.

Mengen sind für viele Programmierer (insbesondere solche, die C(++/#) gewöhnt sind) ungewohntes Terrain. Dabei kann man mit ihnen vieles deutlich eleganter als mit bitweisen Operatoren lösen.
Delphi-Quellcode:
function ShowOptions(options: TMyOptions): string;
var
  opt: TMyOptions;
  optVal: Integer;
begin
  Result := '';
  for opt := opt1 to opt5 do
    if opt in options then
      Result := Result + IfThen(Result='','',',') + IntToStr(Ord(opt));
  Result := '[' + Result + ']';
end;
finde ich einfach schöner als
Delphi-Quellcode:
function ShowOptions(options: TMyOptions): string;
var
  opt: TMyOptions;
  optVal: Integer; // 2 hoch opt
begin
  Result := '';
  optVal := 1;
  for opt := opt1 to opt5 do
  begin
    if (Byte(options) and optVal) <> 0 then
      Result := Result + IfThen(Result='','',',') + IntToStr(Ord(opt));
    optVal := optVal shl 1; // verdoppeln, bitwise-style
  end;
  Result := '[' + Result + ']';
end;
Mit anderen Worten: Pascal-Sets liegen eine Abstraktionsebene höher als die bitweisen Operatoren. Der Programmierer kann mit einfachen (und daher weniger fehleranfälligen) Aufzählungstypen und Mengenoperatoren arbeiten und das systemnahe Hantieren mit Zweierpotenzen (man sehe sich mal die Werte vieler Windows-Konstanten an - 1,2,4,8,...) getrost dem Compiler überlassen.
  Mit Zitat antworten Zitat