On dit que lorsque nous avons une classe Point
et sait comment effectuer point * 3
comme ce qui suit:Dans Ruby, comment fonctionne réellement coerce()?
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
Sortie:
#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
mais,
3 * point
n'est pas entendu:
Point
ne peut pas être contraintesFixnum
(TypeError
)
Nous devons donc définir davantage une méthode d'instance coerce
:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Sortie:
#<Point:0x3c45a88 @x=3, @y=6>
Il est donc dit que 3 * point
est le même que 3.*(point)
. Autrement dit, la méthode d'instance *
prend un argument point
et invoque l'objet 3
.
Maintenant, puisque cette méthode *
ne sait pas comment multiplier un point, donc
point.coerce(3)
sera appelé, et retourner un tableau:
[point, 3]
puis *
est une fois à nouveau appliqué à cela, est-ce vrai?
Maintenant, cela est compris et nous avons maintenant un nouvel objet Point
, tel que réalisé par la méthode d'instance *
de la classe Point
.
La question est:
Qui invoque
point.coerce(3)
? Est-ce Ruby automatiquement, ou est-ce un code à l'intérieur de*
méthode deFixnum
en attrapant une exception? Ou est-ce parcase
déclaration que quand il ne connaît pas l'un des types connus, alors appelezcoerce
?Est-ce que
coerce
doit toujours retourner un tableau de 2 éléments? Peut-il être aucun tableau? Ou peut-il être un tableau de 3 éléments?Et est la règle que l'opérateur (ou la méthode) d'origine
*
sera alors invoqué sur l'élément 0, avec l'argument de l'élément 1? (L'élément 0 et l'élément 1 sont les deux éléments de ce tableau renvoyés parcoerce
.) Qui le fait? Est-ce que c'est fait par Ruby ou est-ce fait par code dansFixnum
? Si c'est fait par code enFixnum
, alors c'est une "convention" que tout le monde suit en faisant une coercition?Ainsi pourrait-il être le code
*
deFixnum
faire quelque chose comme ceci: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
Il est vraiment difficile d'ajouter quelque chose à la méthode de l'instance
Fixnum
coerce
? Il a déjà beaucoup de code et nous ne pouvons pas ajouter quelques lignes à l'améliorer (mais nous voulons jamais?)Le
coerce
dans la classePoint
est tout à fait générique et il fonctionne avec*
ou+
parce qu'ils sont transitifs. Que faire s'il est pas transitive, comme si nous définissons point moins Fixnum être:point = Point.new(100,100) point - 20 #=> (80,80) 20 - point #=> (-80,-80)
C'est une excellente question! Je suis tellement content que je l'ai trouvé parce que cela m'ennuyait et jusqu'à maintenant, je ne pensais pas que c'était résoluable! – sandstrom
Une bonne question. Merci de l'avoir mis. Cela fera économiser beaucoup d'heures de confusion à l'ingénieur, j'en suis sûr. – VaidAbhishek