In Ruby, wie funktioniert zwingen() tatsächlich funktioniert?
Es wird gesagt, dass, wenn wir eine Klasse Point
und weiß, wie man führen point * 3
wie folgt:
class Point
def initialize(x,y)
@x, @y = x, y
end
def *(c)
Point.new(@x * c, @y * c)
end
end
point = Point.new(1,2)
p point
p point * 3
Ausgabe:
#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
aber dann,
3 * point
nicht verstanden:
Point
nicht genötigt inFixnum
(TypeError
)
Also brauchen wir, um weiter zu definieren, eine Instanz-Methode coerce
:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Ausgabe:
#<Point:0x3c45a88 @x=3, @y=6>
Also es wird gesagt, dass 3 * point
ist das gleiche wie 3.*(point)
. Das heißt, die Instanz-Methode *
argument point
und rufen auf dem Objekt 3
.
Nun, da diese Methode *
weiß nicht, wie zu multiplizieren, ein Punkt, so
point.coerce(3)
genannt werden, und sich wieder ein array:
[point, 3]
dann *
ist wieder einmal angewendet, ist das wahr?
Nun, dies ist verstanden und wir haben jetzt eine neue Point
Objekt, wie ausgeführt, durch die Instanz-Methode *
des Point
Klasse.
Die Frage ist:
-
Wer ruft
point.coerce(3)
? Ist es Ruby automatisch, oder ist es einige code innerhalb von*
Methode derFixnum
durch das fangen einer exception? Oder ist es durchcase
Aussage, dass, wenn Sie nicht wissen, einer der bekannten Arten, dann rufen Siecoerce
? -
Tut
coerce
immer brauchen, um wieder ein array mit 2 Elementen? Kann es sein, kein array? Oder kann es sein, ein array mit 3 Elementen? -
Und ist die Regel, dass der original-operator (oder Methode)
*
wird dann aufgerufen werden, die auf element 0, mit dem argument des Elements 1? (Element 0 und element 1 sind die zwei Elemente im array zurückgegebencoerce
.) Wer tut es? Ist es getan, von Ruby oder ist es getan, indem Sie code inFixnum
? Wenn es durch code inFixnum
, dann ist es eine "Konvention", dass ein jeder folgt, wenn dabei ein Zwang?Könnte es also sein, den code in
*
vonFixnum
etwas wie das zu tun:class Fixnum def *(something) if (something.is_a? ...) else if ... # other type /class else if ... # other type /class else # it is not a type /class I know array = something.coerce(self) return array[0].*(array[1]) # or just return array[0] * array[1] end end end
-
So ist es wirklich schwer etwas hinzufügen
Fixnum
's Instanz-Methodecoerce
? Es hat bereits eine Menge code und wir können nicht fügen Sie einfach ein paar Zeilen zu erweitern (aber werden wir das jemals wollen?) -
Den
coerce
imPoint
Klasse ist generisch gehalten und es funktioniert mit*
oder+
weil Sie transitiv. Was ist, wenn es ist nicht transitiv, wie wenn wir definieren Punkt minus Fixnum werden:point = Point.new(100,100) point - 20 #=> (80,80) 20 - point #=> (-80,-80)
- Das ist eine ausgezeichnete Frage! Ich bin so glücklich, ich habe es gefunden weil es mich stört, und bis gerade jetzt ich glaube nicht, es war lösbar!
- Eine sehr gute Frage. Vielen Dank für die Umsetzung es. Es spart viele Ingenieur-Verwirrung-Stunden, da bin ich sicher.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Kurze Antwort: schauen Sie sich wie
Matrix
ist, es zu tun.Die Idee ist, dass
coerce
zurück[equivalent_something, equivalent_self]
, woequivalent_something
ein Objekt ist im Grunde gleichwertig zumsomething
aber, wie man weiß, tun Operationen auf IhremPoint
Klasse. In derMatrix
lib, konstruieren wir eineMatrix::Skalar
von jedemNumeric
Objekt, und die Klasse weiß, wie man Operationen aufMatrix
undVector
.Um Ihre Punkte:
Ja, es ist Ruby direkt (check Aufrufe
rb_num_coerce_bin
in der Quelle), obwohl Ihre eigenen Typen sollte es auch tun, wenn Sie wollen, dass Ihr code erweiterbar durch andere. Zum Beispiel, wenn IhrPoint#*
übergeben wird ein argument nicht erkennt, wird Sie bitten, das argument zucoerce
sich einPoint
durch den Aufrufarg.coerce(self)
.Ja, es muss ein Array mit 2 Elementen, so dass
b_equiv, a_equiv = a.coerce(b)
Ja. Ruby macht es für builtin-Typen, und Sie sollten auch auf Ihre eigenen benutzerdefinierten Typen, wenn Sie möchten, erweiterbar:
Die Idee ist, dass Sie sollten nicht ändern
Fixnum#*
. Wenn Sie nicht wissen, was zu tun ist, zum Beispiel, weil das argument ist einPoint
, dann wird es bitten, Sie durch den AufrufPoint#coerce
.Transitivität (oder eigentlich commutativity) ist nicht notwendig, da der operator wird immer dann aufgerufen, in der richtigen Reihenfolge. Es ist nur der Aufruf
coerce
die vorübergehend kehrt das empfangen und das argument. Es gibt keinen eingebauten Mechanismus, der gewährleistet, commutativity von Operatoren wie+
,==
usw...Wenn man eine knappe, präzise und verständliche Beschreibung zur Verbesserung der Dokumentation, um einen Kommentar!
a - b
ist das gleiche wie-(b - a)
oder so etwas, nicht einmala + b == b + a
. Was macht Sie glaube, ich bin falsch? Haben Sie den MRT-Quelle? Warum nicht versuchen, zu Folgen, die Richtung, die ich angeben?coerce
so dass auch nicht-symmetrischen Operatoren wie-
umgesetzt werden kann in nur eine Richtung, während die symmetrische Operatoren arbeiten auf beide Arten. In anderen Wortena + 3 == 3 + a
und3 + a - 3 == a
aber3 - a
löst einen Fehler aus.Ich finde mich oft das schreiben von code entlang dieses Muster beim Umgang mit commutativity:
[Foo.instantiate(n), self]
wenn es einfach direkt zu konvertieren zahlen in Ihrem format.