为什么 Ruby 的 def +(other) 方法有效? "other" 到底是什么意思?

Why is Ruby's def +(other) method valid? What does "other" actually mean?

我正在练习一个 Ruby 二维坐标运算程序。其中def +(other)def -(other)的写法让我很困惑。我有以下三个问题:

  1. 为什么方法名等于运算符名?
  2. 接收到的参数在哪里?从哪里传来的?
  3. other参数为什么叫other,它的值是多少和 传输过程?

这是代码

class Point
  attr_accessor :x, :y
  
  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def inspect # p印出(x, y)
    "(#{x}, #{y})"
  end

  def +(other)
    self.class.new(x + other.x, y + other.y)
  end

  def -(other)
    self.class.new(x - other.x, y - other.y)
  end
end

point0 = Point.new(3, 6)
point1 = Point.new(1, 8)

p point0
p point1
p point0 + point1
p point0 - point1

它最终会打印

(3, 6)
(1, 8)
(4, 14)
(2, -2)
  1. Why is the method name equal to the operator name?

为什么不呢?为什么用来定义运算符的符号不是用来调用的符号呢?毕竟,我们对所有其他方法都是这样做的:如果我想调用 foo,我定义 def foo.

语言设计者可以选择任意名称。但是让方法的名称与您用来调用它的符号相同是有意义的。你能想象如果语言设计者选择了符号 - 作为对应于 + 运算符的方法名称的混乱吗?

其他编程语言做出不同的选择。例如,in C++, the name of the function corresponding to the + operator is operator+. In Python, the name of the method corresponding to the + operator is __add__。 (更准确地说,当遇到表达式 a + b 时,Python 将首先尝试调用 a.__add__(b),如果 a 没有实现,那么它将尝试“反向添加” b.__radd__(a).)

C#中没有方法名对应运算符,rather, there is an operator keyword后跟运算符对应的符号

如果您想了解有关如何在 Ruby 中计算运算符表达式的所有 nitty-gritty 详细信息,我建议您查看第 11.4.3 节 一元运算符表达式 和第 11.4.4 节 二元运算符表达式 ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification.

  1. Where are the parameters received? Passed from where?

不太清楚你的意思。 参数有点像方法定义中的“洞”或占位符。当您实际调用该方法时,这个“洞”会被 参数 填充。例如,这里:

def foo(a, b) a + b end

ab参数(更准确地说,强制位置参数,它们是mandatory 因为你必须为它们传递一个参数并且它们是 positional 因为哪个参数绑定到参数列表中的哪个参数取决于参数的位置参数列表中的参数)。您实际上不知道此方法的结果是什么,因为您不知道 ab 是什么。它们是值的占位符,将在您调用该方法时填充:

foo(2, 3)

这里,23参数(更准确地说,位置参数)。参数 2 绑定到参数 a,因为 2 是参数列表中的第一个位置参数,而 a 是参数列表中的第一个位置参数。参数 3 绑定到参数 b 因为 3 是参数列表中的第二个位置参数,而 b 是参数列表中的第二个位置参数。

因此,最终为这个特定方法调用执行的代码是(将a替换为2,将b替换为3):

2 + 3

请注意:这是一个简化的解释。用参数表达式替换方法定义主体中每次出现的参数的心智模型是一个很好的近似值,但实际上并不是 Ruby 所做的。特别是,我刚才描述的心智模型对应于 call-by-name evaluation strategy, whereas Ruby actually uses a special case of the call-by-value evaluation strategy called call-by-object-sharing.

你可以观察这段代码的区别:

def bar(a) a + a end

bar((puts "Hello"; 23))
# Hello
#=> 46

在对应于 call-by-name 的“用参数表达式替换参数的每次出现”心智模型中,代码如下所示:

(puts "Hello"; 23) + (puts "Hello"; 23)
# Hello
# Hello
#=> 46

Hello会打印两次。

然而,对于 call-by-value 和 call-by-object-sharing,参数表达式在调用方法之前被评估,评估的结果被传入,所以实际的代码执行看起来更像这样:

__fresh_variable_with_unspeakable_name__ = (puts "Hello"; 23)
# Hello

__fresh_variable_with_unspeakable_name__ + __fresh_variable_with_unspeakable_name__
#=> 46

如果您想了解有关如何在 Ruby 中评估方法参数的所有 nitty-gritty 详细信息,我建议您查看第 11.3 节 方法调用表达式 ,特别是 ISO/IEC 30170:2012 [=] 的第 11.3.1 节 一般描述 和第 11.3.2 节 方法参数 110=]信息技术-编程语言-Ruby规范。

The Ruby Spec Suite aka ruby/spec, in particular in language/def_spec.rb and language/method_spec.rb 中还可以找到一些(不幸的是不完整的)信息。

  1. Why is the parameter other called other, and what is its value and transmission process?

参数 other 被称为 other 是因为那段代码的作者选择这样称呼它。他们可以称它为 abxyfoobari_dont_have_the_slightest_idea_what_to_call_this_parameter_so_i_am_just_choosing_something_totally_random。如果你想知道为什么那段代码的作者选择调用它other,你就得问那段代码的作者

]

other 是二元运算符定义的“其他”操作数的一个有点流行的名称,而不仅仅是在 Ruby 中,例如您可以在 Python 文档中看到以及。如果你大声朗读它确实有意义:在像 Ruby 或 Python 这样的 Object-Oriented 编程语言中,运算符被解释为被发送到操作数之一,两个操作数之一二元运算符的操作数将始终是 selfthis 和 th“其他”操作数将是……好吧……“其他”操作数。因此,将其命名为 other 是很自然的。在 Ruby 中,这被编入了一些风格指南,例如 Rubocop Ruby Style Guide.