在Ruby中,coerce()实际如何工作? [英] In Ruby, how does coerce() actually work?

查看:159
本文介绍了在Ruby中,coerce()实际如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据说当我们有一个类Point并知道如何执行point * 3时,如下所示:

It is said that when we have a class Point and knows how to perform point * 3 like the following:

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)

Point can't be coerced into Fixnum (TypeError)

因此,我们需要进一步定义实例方法coerce:

So we need to further define an instance method coerce:

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

p 3 * point

输出:

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

因此,可以说3 * point3.*(point)相同.也就是说,实例方法*接受参数point并在对象3上调用.

So it is said that 3 * point is the same as 3.*(point). That is, the instance method * takes an argument point and invoke on the object 3.

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

Now, since this method * doesn't know how to multiply a point, so

point.coerce(3)

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

will be called, and get back an array:

[point, 3]

然后再次将*应用于它,对吗?

and then * is once again applied to it, is that true?

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

Now, this is understood and we now have a new Point object, as performed by the instance method * of the Point class.

问题是:

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

  1. Who invokes point.coerce(3)? Is it Ruby automatically, or is it some code inside of * method of Fixnum by catching an exception? Or is it by case statement that when it doesn't know one of the known types, then call coerce?

coerce是否总是需要返回2个元素的数组?可以没有数组吗?还是可以是3个元素组成的数组?

Does coerce always need to return an array of 2 elements? Can it be no array? Or can it be an array of 3 elements?

这是规则,即原始运算符(或方法)*然后将使用元素1的参数在元素0上被调用吗? (元素0和元素1是coerce返回的该数组中的两个元素.)是谁?是由Ruby完成还是由Fixnum中的代码完成?如果是通过Fixnum中的代码完成的,那么这是每个人在强制执行时都会遵循的惯例"吗?

And is the rule that, the original operator (or method) * will then be invoked on element 0, with the argument of element 1? (Element 0 and element 1 are the two elements in that array returned by coerce.) Who does it? Is it done by Ruby or is it done by code in Fixnum? If it is done by code in Fixnum, then it is a "convention" that everybody follows when doing a coercion?

那么Fixnum*中的代码是否可以执行以下操作:

So could it be the code in * of Fixnum doing something like this:

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中添加一些内容?它已经有很多代码,我们不能仅仅添加几行代码来增强它(但是我们会想要吗?)

  • So it is really hard to add something to Fixnum's instance method coerce? It already has a lot of code in it and we can't just add a few lines to enhance it (but will we ever want to?)

    Point类中的coerce非常通用,并且可与*+一起使用,因为它们是可传递的.如果它不是可传递的,例如将Point减去Fixnum定义为:

    The coerce in the Point class is quite generic and it works with * or + because they are transitive. What if it is not transitive, such as if we define Point minus Fixnum to be:

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

  • 推荐答案

    简短答案:签出

    Short answer: check out how Matrix is doing it.

    这个想法是coerce返回[equivalent_something, equivalent_self],其中equivalent_something是基本上等同于something的对象,但是知道如何对Point类进行操作.在Matrix库中,我们构造了 Matrix::Scalar 来自任何Numeric对象,并且该类知道如何对MatrixVector执行操作.

    The idea is that coerce returns [equivalent_something, equivalent_self], where equivalent_something is an object basically equivalent to something but that knows how to do operations on your Point class. In the Matrix lib, we construct a Matrix::Scalar from any Numeric object, and that class knows how to perform operations on Matrix and Vector.

    要解决您的问题,

    1. 是的,直接是Ruby(检查对rb_num_coerce_bin(在源代码中)),但是如果您希望代码可以被其他人扩展,那么您自己的类型也应该这样做.例如,如果您的Point#*传递了一个无法识别的参数,则您可以通过调用arg.coerce(self)Point询问coerce本身的参数.

    1. Yes, it is Ruby directly (check calls to rb_num_coerce_bin in the source), although your own types should do too if you want your code to be extensible by others. For example if your Point#* is passed an argument it doesn't recognize, you would ask that argument to coerce itself to a Point by calling arg.coerce(self).

    是的,它必须是2个元素组成的数组,这样b_equiv, a_equiv = a.coerce(b)

    Yes, it has to be an Array of 2 elements, such that b_equiv, a_equiv = a.coerce(b)

    是的. Ruby针对内置类型执行此操作,如果您希望扩展性,也应该使用自己的自定义类型:

    Yes. Ruby does it for builtin types, and you should too on your own custom types if you want to be extensible:

    def *(arg)
      if (arg is not recognized)
        self_equiv, arg_equiv = arg.coerce(self)
        self_equiv * arg_equiv
      end
    end
    

  • 这个想法是,您不应该修改Fixnum#*.如果它不知道要做什么,例如因为参数为Point,那么它将通过调用Point#coerce来询问您.

  • The idea is that you shouldn't modify Fixnum#*. If it doesn't know what to do, for example because the argument is a Point, then it will ask you by calling Point#coerce.

    不需要传递性(或实际上是可交换性),因为始终以正确的顺序调用运算符.只是对coerce的调用会临时还原接收到的参数.没有内置的机制可以确保+==等运算符的可交换性.

    Transitivity (or actually commutativity) is not necessary, because the operator is always called in the right order. It's only the call to coerce which temporarily reverts the received and the argument. There is no builtin mechanism that insures commutativity of operators like +, ==, etc...

    如果有人可以提出简洁,准确和清晰的描述来改进官方文档,请发表评论!

    If someone can come up with a terse, precise and clear description to improve the official documentation, leave a comment!

    这篇关于在Ruby中,coerce()实际如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆