Die Programmiersprache Ruby (German Edition)
Weg, für so gut wie jede Klasse perfekte Hashcodes zu erzeugen: Kombinieren Sie einfach die Hashcodes aller Objekte, die durch Ihre Klasse referenziert werden. (Genauer gesagt: Kombinieren Sie die Hashcodes aller Objekte, die von Ihrer Methode
eql?
verglichen werden.) Der Trick ist, die Hashcodes richtig zu kombinieren. Die folgende Methode
hash
ist keine gute:
def hash
@x.hash + @y.hash
end
Das Problem bei dieser Methode ist, dass sie denselben Hashcode für den Punkt
(1,0)
wie für den Punkt
(0,1)
zurückgibt. Das ist zwar erlaubt, führt aber zu einer schlechten Performance, wenn Punkte als Hash-Schlüssel genutzt werden. Stattdessen sollten wir das Ganze ein wenig durchmischen:
def hash
code = 17
code = 37*code + @x.hash
code = 37*code + @y.hash
# Weitere ähnliche Zeilen für jede wichtige Instanzvariable
code # Rückgabe des Ergebnisses
end
Dieses allgemein einsetzbare Rezept für Hashcodes sollte für die meisten Ruby-Klassen ausreichend sein. Das Vorgehen und die Konstanten 17 und 37 wurden aus dem Buch Effective Java von Joshua Bloch (Prentice Hall) übernommen.
7.1.10 Punkte ordnen
Was wäre, wenn wir eine Ordnung für
Point
-Objekte definieren möchten, um sie vergleichen und sortieren zu können? Es gibt eine Reihe von Möglichkeiten, Punkte zu ordnen, aber wir wollen uns dafür entscheiden, sie anhand ihres Abstands vom Ursprung anzuordnen. Dieser Abstand (oder Betrag) wird über den Satz von Pythagoras berechnet: die Wurzel der Summe der Quadrate der x - und y -Koordinaten.
Um diese Ordnung für
Point
-Objekte zu definieren, müssen wir nur den Operator
<=>
definieren (siehe „4.6.6 Vergleich: <, <=, >, >= und <=>“ ) und das Modul
Comparable
mit aufnehmen. Damit werden Implementierungen der Gleichheits- und relationalen Operatoren hineingemischt, die auf unserer Implementierung des von uns definierten allgemeinen Operators
<=>
basieren. Der Operator
<=>
sollte
self
mit dem ihm übergebenen Objekt vergleichen. Wenn
self
kleiner als dieses Objekt ist (hier also näher am Ursprung), sollte er
−1
zurückgeben. Wenn beide Objekte gleich sind, sollte er
0
liefern. Und wenn
self
größer als das Argumentobjekt ist, muss die Funktion
1
zurückgeben. (Die Methode sollte
nil
liefern, wenn das Argumentobjekt und
self
Typen sind, die sich nicht vergleichen lassen.) Der folgende Code ist unsere Implementierung von
<=>
. Es gibt dabei zwei Dinge, die erwähnenswert sind. Zum einen wird die Methode
Math.sqrt
geflissentlich ignoriert und einfach die Summe der Quadrate der Koordinaten verglichen. Zum anderen wird nach dem Berechnen der Summe der Quadrate einfach an den Operator
<=>
der Klasse
Float
delegiert:
include Comparable # Einmischen aus dem Modul Comparable
# Definieren einer Ordnung für Punkte anhand ihres Abstands vom Ursprung
# Diese Methode ist für das Modul Comparable erforderlich.
def <=>(other)
return nil unless other.instance_of? Point
@x**2 + @y**2 <=> other.x**2 + other.y**2
end
Beachten Sie, dass das Modul
Comparable
eine Methode
==
definiert, die unsere Definition von
<=>
verwendet. Unser abstandsbasierter Vergleichsoperator führt zu einer Methode
==
, die die Punkte
(1,0)
und
(0,1)
als gleich behandelt. Da unsere Klasse
Point
allerdings explizit ihre eigene Methode
==
definiert, wird die Methode
==
von
Comparable
niemals aufgerufen. Idealerweise sollten die Operatoren
==
und
<=>
eine konsistente Definition von Gleichheit besitzen. Das war bei unserer Klasse
Point
nicht möglich, daher landen wir nun bei Operatoren, die das Folgende ermöglichen:
p,q = Point.new(1,0), Point.new(0,1)
p == q # => false: p ist nicht gleich q
p < q # => false: p ist nicht größer als q
p > q # => false: p ist nicht kleiner als q
Abschließend lohnt es sich, darauf hinzuweisen, dass das Modul
Enumerable
viele Methoden definiert, unter anderem
sort
,
min
und
max
, die nur nutzbar sind, wenn die enumerierten Objekte den Operator
<=>
definieren.
7.1.11 Ein veränderbarer Point
Objekte der Klasse
Point
, die wir bisher entwickelt haben, sind unveränderbar : Nachdem ein Punktobjekt erzeugt wurde, gibt es keine öffentliche API, um die x - und y -Koordinaten dieses Punkts zu ändern. Das ist vermutlich auch das, was wir haben wollen. Aber lassen Sie uns ein wenig abschweifen und ein paar zusätzliche Methoden entwickeln, die für veränderbare Punkte nützlich sind.
Vor allem benötigen wir Setter-Methoden
x=
und
y=
, um die x- und y-Koordinaten direkt setzen zu können. Wir könnten diese Methoden
Weitere Kostenlose Bücher