在Ruby中,强制()实际上是如何工作的?

在Ruby中,强制()实际上是如何工作的?,ruby,coercion,coerce,type-coercion,Ruby,Coercion,Coerce,Type Coercion,据说,当我们有一个类点并且知道如何执行点*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,

据说,当我们有一个类
并且知道如何执行
点*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>
#<Point:0x3c45a88 @x=3, @y=6>
不理解:

不能强制为
Fixnum
TypeError

因此,我们需要进一步定义一个实例方法
强制

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

p 3 * point
输出:

#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>
#<Point:0x3c45a88 @x=3, @y=6>
将被调用,并获取一个数组:

[point, 3]
然后,
*
再次应用于它,是真的吗

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

问题是:

  • 谁调用
    点。强制(3)
    ?它是Ruby自动生成的,还是通过捕获异常在
    Fixnum
    *
    方法中生成的代码?或者是通过
    case
    语句,当它不知道某个已知类型时,调用
    强制

  • 强制
    是否总是需要返回2个元素的数组?它可以是无阵列吗?或者它可以是一个由3个元素组成的数组

  • 规则是,原始运算符(或方法)
    *
    将在元素0上调用,参数为元素1吗?(元素0和元素1是由
    强制
    返回的数组中的两个元素)是谁执行的?它是由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
    的实例方法
    concure
    添加一些内容确实很困难?它已经有很多代码在里面了,我们不能仅仅添加几行代码来增强它(但是我们会想要吗?)

  • 类中的
    强制
    非常通用,它与
    *
    +
    一起工作,因为它们是可传递的。如果它不是可传递的,比如我们将点减去Fixnum定义为:

    point = Point.new(100,100)
    point - 20  #=> (80,80)
    20 - point  #=> (-80,-80)
    
  • 简短回答:退房

    其思想是,
    强制
    返回
    [等价物,等价物本身]
    ,其中
    等价物
    是一个基本上等价于
    某物
    的对象,但它知道如何对
    类执行操作。在
    矩阵
    库中,我们从任何
    数值
    对象构造一个类,该类知道如何对
    矩阵
    向量
    执行操作

    针对您的观点:

  • 是的,它是Ruby directly(check calls to),不过如果您希望自己的代码能够被其他人扩展,您自己的类型也应该这样做。例如,如果您的
    点#*
    被传递了一个它无法识别的参数,您可以通过调用
    arg.concure(self)
    要求该参数将
    自身强制到

  • 是的,它必须是一个由2个元素组成的数组,这样
    b_equiv,a_equiv=a.胁迫(b)

  • 对。Ruby对内置类型执行此操作,如果您希望扩展,也应该对自己的自定义类型执行此操作:

    def *(arg)
      if (arg is not recognized)
        self_equiv, arg_equiv = arg.coerce(self)
        self_equiv * arg_equiv
      end
    end
    
  • 其思想是您不应该修改
    Fixnum.*
    。如果它不知道该做什么,例如因为参数是
    ,那么它将通过调用
    点#强制
    来询问您

  • 传递性(或者实际上是交换性)是不必要的,因为操作符总是按正确的顺序调用的。只有对
    强制
    的调用才会临时恢复接收到的和参数。没有任何内置机制可以确保运算符的可交换性,如
    +
    ==


  • 如果有人能提出一个简洁、准确、清晰的描述来改进官方文档,请留下评论

    在处理可交换性时,我发现自己经常按照这种模式编写代码:

    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
    
    class-Foo
    def启动(某些状态)
    #...
    结束
    定义/(n)
    #处理Foo/n的代码
    结束
    def*(n)
    #处理Foo*n的代码
    结束
    def强制(n)
    [reversefo.new(某些州),n]
    结束
    结束
    类反转Foo
    hm,及物性真的有区别吗?例如,请参见“否”,及物性不起任何作用,Ruby也不认为
    a-b
    -(b-a)
    或类似的东西相同,甚至不认为
    a+b==b+a
    。你凭什么相信我错了?你查过核磁共振的来源了吗?为什么不试着按照我指示的方向去做呢?我认为OP的意思是“对称”而不是“传递”。在任何情况下,我都想知道如何编写
    强制
    ,以便像
    -
    这样的非对称运算符只能在一个方向上实现,同时保持对称运算符双向工作。换句话说,
    a+3==3+a
    3+a-3==a
    但是
    3-a
    会引起一个错误。@OldPro所说的“对称”我认为你的意思是“可交换的”。但我同意,OP可能是指交换的,而不是传递的。传递性与关系(如等式)有关,而不是加减运算。没有任何东西可以保证可交换性。即使不涉及胁迫。例如,这是一个很好的问题!我很高兴我找到了它,因为这一直困扰着我,直到现在我还不认为它是可以解决的!一个很好的问题。谢谢你把它放在这里。我敢肯定,这将节省许多工程师困惑的时间。对于将来阅读本文的任何人:我认为如果可以轻松地将数字直接转换为您的格式,则更推荐使用
    [Foo.instantiate(n),self]