据说当我们上课时 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 班级。

问题是:

  1. 谁调用 point.coerce(3)?是自动的红宝石吗,还是在内部的某些代码 * 的方法 Fixnum 通过抓住例外?还是由 case 声明说当它不知道已知类型之一时,请致电 coerce?

  2. coerce 总是需要返回2个元素的数组?可以不是数组吗?还是可以是3个元素的数组?

  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
    
  4. 因此,很难添加一些东西 Fixnum的实例方法 coerce?它已经有很多代码,我们不能仅添加几行来增强它(但是我们会想要吗?)

  5. 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 对象,该课程知道如何执行操作 MatrixVector.

解决您的观点:

  1. 是的,直接是Ruby(检查电话 rb_num_coerce_bin 在来源),尽管如果您希望代码可以扩展他人,那么您自己的类型也应该这样做。例如,如果您的 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 这暂时恢复了收到的和论点。没有内置机制可以确保运营商的通勤性 +, ==, , 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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top