Se dice que cuando tenemos una clase Point
y sabe cómo realizar point * 3
como la siguiente:En Ruby, ¿cómo funciona realmente 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
Salida:
#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
pero luego,
3 * point
no es entendido:
Point
no puede ser obligado aFixnum
(TypeError
)
Así que tenemos que definir con más detalle un método de instancia coerce
:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Salida:
#<Point:0x3c45a88 @x=3, @y=6>
lo tanto, es dijo que 3 * point
es lo mismo que 3.*(point)
. Es decir, el método de instancia *
toma un argumento point
e invoca en el objeto 3
.
Ahora, ya que este método *
no sabe cómo multiplicar un punto, por lo
point.coerce(3)
se llamará, y volver una matriz:
[point, 3]
y luego *
es una vez de nuevo aplicado a él, ¿es cierto?
Ahora, esto se entiende y ahora tenemos un nuevo Point
objeto, como se realizó por el método de instancia *
de la clase Point
.
La pregunta es:
que invoca
point.coerce(3)
? ¿Es Ruby automáticamente, o es algún código dentro del método*
deFixnum
al detectar una excepción? ¿O es por la declaracióncase
que cuando no conoce uno de los tipos conocidos, llama alcoerce
?¿El
coerce
siempre debe devolver una matriz de 2 elementos? ¿No puede ser una matriz? ¿O puede ser una matriz de 3 elementos?¿Y es la regla que, el operador original (o método)
*
se invocará entonces en el elemento 0, con el argumento del elemento 1? (Elemento 0 y elemento 1 son los dos elementos en esa matriz devueltos porcoerce
.) ¿Quién lo hace? ¿Lo hace Ruby o está hecho por código enFixnum
? Si se hace por código enFixnum
, entonces ¿es una "convención" que todos siguen al hacer una coacción?Así podría ser el código en
*
deFixnum
hacer algo como esto: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
lo tanto, es muy difícil añadir algo al método
Fixnum
's ejemplocoerce
? Ya cuenta con una gran cantidad de código en él y no podemos simplemente añada unas cuantas líneas para mejorar (pero nunca vamos a querer?)El
coerce
en la clasePoint
es bastante genérico y funciona con*
o+
porque son transitivos. ¿Y si no es transitivo, como si definimos punto negativo Fixnum sean:point = Point.new(100,100) point - 20 #=> (80,80) 20 - point #=> (-80,-80)
Esta es una excelente pregunta! Estoy tan feliz de haberlo encontrado porque esto me ha estado molestando y hasta ahora no creía que pudiera resolverse. – sandstrom
Una gran pregunta. Gracias por decirlo. Ahorrará muchas horas de confusión de ingenieros, estoy seguro. – VaidAbhishek