Говорят, что когда мы имеем класс Point
и знаем, как выполнить point * 3
следующим образом:
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
Вывод:
#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
но затем
3 * point
не понимается:
Point
не может быть принудительно введен вFixnum
(TypeError
)
Итак, нам нужно дополнительно определить метод экземпляра coerce
:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Вывод:
#<Point:0x3c45a88 @x=3, @y=6>
Итак, говорят, что 3 * point
совпадает с 3.*(point)
. То есть метод экземпляра *
принимает аргумент Point
и вызывает объект 3
.
Теперь, поскольку этот метод *
не знает, как умножить точку, поэтому
point.coerce(3)
будет вызываться и вернуть массив:
[point, 3]
а затем *
снова применяется к нему, это правда?
Теперь это понимается, и теперь у нас есть новый объект Point
, который выполняется методом экземпляра *
класса Point
.
Возникает вопрос:
-
Кто вызывает
point.coerce(3)
? Является ли это Ruby автоматически, или это какой-то код внутри метода*
Fixnum
, вылавливая исключение? Или это операторcase
, что, когда он не знает один из известных типов, вызовитеcoerce
? -
Требуется ли
coerce
вернуть массив из двух элементов? Разве это не массив? Или это может быть массив из трех элементов? -
И является ли правило, что исходный оператор (или метод)
*
будет вызываться в элементе 0 с аргументом элемента 1? (Элемент 0 и элемент 1 - это два элемента в этом массиве, возвращаемыеcoerce
.) Кто это? Это делается Ruby или выполняется с помощью кода вFixnum
? Если это делается с помощью кода вFixnum
, то это "соглашение", которое каждый следует при принуждении?Так может ли быть код в
*
ofFixnum
делать что-то вроде этого: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
-
Так что действительно сложно добавить что-то к
Fixnum
методу экземпляраcoerce
? В нем уже много кода, и мы не можем просто добавить несколько строк для его улучшения (но будем ли мы когда-либо хотеть?) -
coerce
в классеPoint
является довольно общим и работает с*
или+
, потому что они являются транзитивными. Что, если это не транзитивно, например, если мы определяем Point минус Fixnum следующим образом:point = Point.new(100,100) point - 20 #=> (80,80) 20 - point #=> (-80,-80)