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.