Ruby运算符方法调用与普通方法调用

我想知道为什么调用操作符方法不需要点? 或者更确切地说,为什么不能在没有点的情况下调用普通方法?

 Foo级
   def +(对象)
    把“这会起作用”
  结束
   def plus(对象)
    把“这不会”
  结束
结束 
 f = Foo.new
 f +“任何事情”#“这会起作用”
 f加“任何”#NoMethodError:未定义的方法`plus'表示main:Object

该实现没有允许新运算符的通用定义所需的额外复杂性。

相反,Ruby有一个Yacc解析器,它使用静态定义的语法。 你得到了内置的运营商,就是这样。 符号出现在语法中的一组固定句子中。 正如您所指出的,运营商可能会超载,这比大多数语言都要多。

当然不是因为Matz很懒。

Ruby实际上有一个非常复杂的语法,大致处于Yacc可以实现的极限。 要想变得更复杂,需要使用不太便携的编译器生成器,或者需要在C中手动编写解析器,这样做会以自己的方式限制未来的实现可移植性,并且不向世界提供Yacc输入。 这将是一个问题,因为Ruby的Yacc源代码是唯一的Ruby语法文档,因此是“标准”。

关于这个问题的答案,就几乎每个语言设计问题而言:“只是因为”。 语言设计是一系列主要的主观权衡。 对于大多数这些主观权衡,对于为什么事情就是这样的问题, 唯一正确的答案就是“因为Matz这么说”。

当然还有其他选择:

  • Lisp根本就没有运营商。 +-::>=等等只是普通的合法函数名称(实际上是变量名称),就像foobar?

     (plus 1 2) (+ 1 2) 
  • Smalltalk 几乎没有运营商。 Smalltalk唯一的特殊shell是,只包含操作符的方法不必以冒号结尾。 特别是,由于没有运算符,所有方法调用都具有相同的优先级,并且严格从左到右进行评估: 2 + 3 * 420 ,而不是14

     1 plus: 2 1 + 2 
  • Scala 几乎没有运营商。 就像Lisp和Smalltalk一样, *-#:::等等只是合法的方法名称。 (实际上,它们也是合法的类,特征,类型和字段名称。) 任何方法都可以使用或不使用点调用。 如果您使用没有点的表单,并且该方法只接受一个参数,那么您也可以不使用括号。 尽管Scala不是用户可定义的,但它确实具有优先权; 它只是由名字的第一个字符决定。 作为一个额外的转折,以冒号结尾的运算符方法名称是反转的或右关联的,即a :: b等同于a :: b b.::(a)而不是a.::(b)

     1.plus(2) 1 plus(2) 1 plus 2 1.+(2) 1 +(2) 1 + 2 
  • 在Haskell中,任何名称由运算符符号组成的函数都被视为运算符。 通过将任何函数括在反引号中,可以将任何函数视为运算符,并且可以通过将任何运算符括在括号中来将其视为函数。 此外,程序员可以自由地为用户定义的运算符定义关联性,固定性和优先级。

     plus 1 2 1 `plus` 2 (+) 1 2 1 + 2 

没有特别的理由说明Ruby不能以类似于Scala的样式支持用户定义的运算符。 Ruby之所以不能支持操作员位置的任意方法,只是因为

 foo plus bar 

已经合法,因此这将是一个向后不兼容的变化。

另一件需要考虑的事情是Ruby实际上并没有事先完全设计好。 它是通过实施而设计的。 这意味着在很多地方,实施正在泄漏。 例如,绝对没有逻辑上的原因

 puts(!true) 

是合法的但是

 puts(not true) 

不是。 之所以如此,唯一的原因是因为Matz使用LALR(1)解析器来解析非LALR(1)语言。 如果他设计了这种语言,他就不会首先选择一个LALR(1)解析器,而且这个表达式是合法的。

目前正在ruby-core上讨论的Refinementfunction是另一个例子。 它当前指定的方式将使得无法优化方法调用和内联方法, 即使所讨论的程序实际上根本没有使用Refinement 。 只需进行简单的调整,它就可以表达和强大, 确保只有实际使用 Refinement的范围才会产生悲观化成本。 显然,以这种方式指定的唯一原因是:a)以这种方式原型更容易,并且b)YARV没有优化器,所以没有人甚至不愿意考虑其含义(好吧,除了Charles之外没有人Oliver Nutter)。

所以,对于你对Ruby设计的基本问题,答案几乎总是要么“因为Matz这样说”或“因为在1993年它更容易实现”。

因为Ruby具有“语法糖”,允许为预设情况提供各种方便的语法。 例如:

 class Foo def bar=( o ); end end # This is actually calling the bar= method with a parameter, not assigning a value Foo.new.bar = 42 

这是可以作为Ruby中的方法实现的运算符表达式的列表。

因为Ruby的语法设计看起来大致像流行的OO语言,而那些使用点运算符来调用方法。 它从Smalltalk借用其对象模型的语言并没有使用点作为消息,事实上它有一种相当“怪异”的语法,许多人发现这种语法很不合适。 Ruby被称为“具有Algol语法的Smalltalk”,其中Algol是一种语言,它为我们提供了您在此处讨论的约定。 (当然,实际上存在的差异不仅仅是Algol语法。)

缺少大括号对于ruby 1.8来说是一个“优势”,但是对于ruby 1.9,你甚至不能写method_0 method_1 some param它会被拒绝,所以语言更倾向于严格版本而不是自由forms。