在Ruby中,Coerce()实际上是如何工作的?
-
04-10-2019 - |
题
据说当我们上课时 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)
?是自动的红宝石吗,还是在内部的某些代码*
的方法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
因此,很难添加一些东西
Fixnum
的实例方法coerce
?它已经有很多代码,我们不能仅添加几行来增强它(但是我们会想要吗?)这
coerce
在里面Point
课程非常通用,并且可以与*
或者+
因为它们是传递的。如果它不是传递的,例如我们将点减固定为:point = Point.new(100,100) point - 20 #=> (80,80) 20 - point #=> (-80,-80)
解决方案
简短答案:结帐 如何 Matrix
正在这样做.
想法是 coerce
返回 [equivalent_something, equivalent_self]
, , 在哪里 equivalent_something
基本上等同于 something
但这知道如何对您的操作进行操作 Point
班级。在里面 Matrix
lib,我们构建了 Matrix::Scalar
来自任何 Numeric
对象,该课程知道如何执行操作 Matrix
和 Vector
.
解决您的观点:
是的,直接是Ruby(检查电话
rb_num_coerce_bin
在来源),尽管如果您希望代码可以扩展他人,那么您自己的类型也应该这样做。例如,如果您的Point#*
通过了它不认识的论点,您会要求该论点coerce
本身Point
通过打电话arg.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
想法是您不应该修改
Fixnum#*
. 。例如,如果不知道该怎么办Point
, ,然后打电话问你Point#coerce
.不需要传递性(或实际交换性),因为操作员总是以正确的顺序调用。只是呼吁
coerce
这暂时恢复了收到的和论点。没有内置机制可以确保运营商的通勤性+
,==
, , ETC...
如果有人可以提出简短的,精确和清晰的描述以改善官方文件,请发表评论!
其他提示
我发现自己在处理通勤时经常沿着这种模式编写代码:
class Foo
def initiate(some_state)
#...
end
def /(n)
# code that handles Foo/n
end
def *(n)
# code that handles Foo * n
end
def coerce(n)
[ReverseFoo.new(some_state),n]
end
end
class ReverseFoo < Foo
def /(n)
# code that handles n/Foo
end
# * commutes, and can be inherited from Foo
end