nop*_*ole 60 ruby coercion coerce type-coercion
据说,当我们有一个班级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
Run Code Online (Sandbox Code Playgroud)
输出:
#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
Run Code Online (Sandbox Code Playgroud)
但是之后,
3 * point
Run Code Online (Sandbox Code Playgroud)
不明白:
Point不能强迫进入Fixnum(TypeError)
所以我们需要进一步定义一个实例方法coerce:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Run Code Online (Sandbox Code Playgroud)
输出:
#<Point:0x3c45a88 @x=3, @y=6>
Run Code Online (Sandbox Code Playgroud)
所以据说3 * point是一样的3.*(point).也就是说,instance方法*接受一个参数point并在对象上调用3.
现在,由于这种方法*不知道如何乘以一个点,所以
point.coerce(3)
Run Code Online (Sandbox Code Playgroud)
将被调用,并返回一个数组:
[point, 3]
Run Code Online (Sandbox Code Playgroud)
然后*再次应用它,是真的吗?
现在,这一技术被理解,现在我们有一个新的Point对象,通过实例方法进行*了的Point类.
问题是:
谁调用point.coerce(3)?它是自动的Ruby,还是通过捕获异常的*方法中Fixnum的一些代码?或者通过case声明,当它不知道其中一种已知类型时,然后调用coerce?
是否coerce总是需要回到2个元素的数组?可以没有阵列吗?或者它可以是3个元素的数组?
并且规则是,原始运算符(或方法)*将在元素0上使用元素1的参数调用?(元素0和元素1是返回的数组中的两个元素coerce.)谁做到了?它是由Ruby完成还是由代码完成Fixnum?如果它是通过代码完成的Fixnum,那么每个人在做强制时都会遵循"约定"?
因此,可以把它在代码*中Fixnum做这样的事情:
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
Run Code Online (Sandbox Code Playgroud)所以在Fixnum实例方法中添加一些东西真的很难coerce吗?它已经有很多代码,我们不能只添加几行来增强它(但我们想要吗?)
将coerce在Point类是相当通用的,它的工作原理*或+因为他们是传递的.如果它不是传递的,例如我们将Point减去Fixnum定义为:
point = Point.new(100,100)
point - 20 #=> (80,80)
20 - point #=> (-80,-80)
Run Code Online (Sandbox Code Playgroud)Mar*_*une 42
简短的回答:看看怎么Matrix做.
这个想法是coerce返回[equivalent_something, equivalent_self],其中equivalent_something一个对象基本上等同于something但知道如何对你的Point类进行操作.在Matrixlib中,我们Matrix::Scalar从任何Numeric对象构造一个,并且该类知道如何在Matrix和上执行操作Vector.
为了解决你的观点:
是的,它直接是Ruby(检查rb_num_coerce_bin源代码中的调用),但是如果您希望代码可以被其他人扩展,那么您自己的类型也应该这样做.例如,如果你Point#*传递的是一个它无法识别的参数,你可以通过调用将该参数问coerce自己.Pointarg.coerce(self)
是的,它必须是一个包含2个元素的数组,这样 b_equiv, a_equiv = a.coerce(b)
是.Ruby为内置类型做了它,如果你想要可扩展,你也应该使用自己的自定义类型:
def *(arg)
if (arg is not recognized)
self_equiv, arg_equiv = arg.coerce(self)
self_equiv * arg_equiv
end
end
Run Code Online (Sandbox Code Playgroud)这个想法是你不应该修改Fixnum#*.如果它不知道该怎么做,例如因为参数是a Point,那么它会通过调用来询问你Point#coerce.
传递性(或实际的可交换性)不是必需的,因为操作符总是以正确的顺序调用.它只是coerce暂时恢复收到和参数的调用.没有,从而确保了像运营商的交换性内建机制+,==等等......
如果有人能够提出简洁,准确和清晰的描述来改进官方文档,请发表评论!