gpt4 book ai didi

ruby - 在 Ruby 中,coerce() 是如何工作的?

转载 作者:数据小太阳 更新时间:2023-10-29 06:20:15 26 4
gpt4 key购买 nike

据说当我们有一个类 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 can't be coerced into Fixnum (TypeError)

所以我们需要进一步定义一个实例方法coerce:

class Point
def coerce(something)
[self, something]
end
end

p 3 * point

输出:

#<Point:0x3c45a88 @x=3, @y=6>

所以说3 * point3.*(point)是一样的。也就是说,实例方法 * 接受一个参数 point 并在对象 3 上调用。

现在,由于此方法* 不知道如何乘以一个点,所以

point.coerce(3)

将被调用,并返回一个数组:

[point, 3]

然后 * 再次应用于它,是这样吗?

现在,这已经理解了,我们现在有了一个新的 Point 对象,由 Point 类的实例方法 * 执行。

问题是:

  1. 谁调用了 point.coerce(3)?它是 Ruby 自动生成的,还是通过捕获异常在 Fixnum* 方法中的一些代码?还是通过 case 语句,当它不知道其中一种已知类型时,然后调用 coerce

  2. coerce 是否总是需要返回一个包含 2 个元素的数组?可以没有数组吗?或者它可以是 3 个元素的数组吗?

  3. 规则是,原始运算符(或方法)* 然后将使用元素 1 的参数在元素 0 上调用? (元素 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
  4. 所以向Fixnum 的实例方法coerce 添加一些东西真的很难吗?它已经有很多代码,我们不能只添加几行来增强它(但我们会想要吗?)

  5. Point 类中的coerce 非常通用,它适用于 *+因为它们是可传递的。如果它不是传递性的,例如如果我们将 Point minus Fixnum 定义为:

    point = Point.new(100,100)
    point - 20 #=> (80,80)
    20 - point #=> (-80,-80)

最佳答案

简短回答:查看 how Matrix is doing it .

想法是coerce返回 [equivalent_something, equivalent_self] , 其中equivalent_something是一个基本上等同于 something 的对象但它知道如何对你的 Point 进行操作类(class)。在Matrix lib,我们构造一个 Matrix::Scalar 来自任何 Numeric对象,并且该类知道如何对 Matrix 执行操作和 Vector .

解决您的问题:

  1. 是的,它直接是 Ruby(检查对 rb_num_coerce_bin in the source 的调用),尽管如果您希望其他人可以扩展您的代码,您自己的类型也应该这样做。例如,如果您的 Point#*传递了一个它无法识别的参数,你会向 coerce 询问该参数本身为 Point调用arg.coerce(self) .

  2. 是的,它必须是一个包含 2 个元素的数组,例如 b_equiv, a_equiv = a.coerce(b)

  3. 是的。 Ruby 为内置类型做这件事,如果你想扩展,你也应该为你自己的自定义类型做这件事:

    def *(arg)
    if (arg is not recognized)
    self_equiv, arg_equiv = arg.coerce(self)
    self_equiv * arg_equiv
    end
    end
  4. 想法是您不应该修改 Fixnum#* .如果它不知道该怎么做,例如因为参数是 Point , 然后它会通过调用 Point#coerce 询问您.

  5. 传递性(或者实际上是交换性)不是必需的,因为运算符总是以正确的顺序被调用。这只是对 coerce 的调用暂时恢复接收到的参数。没有内置机制可以确保运算符的可交换性,例如 + , ==等等……

如果有人能想出一个简洁、准确和清晰的描述来改进官方文档,请发表评论!

关于ruby - 在 Ruby 中,coerce() 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2799571/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com