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

发布于 2024-10-06 07:32:13 字数 315 浏览 3 评论 0原文

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

例子

class Foo
  def +(object)
    puts "this will work"
  end
  def plus(object)
    puts "this won't"
  end
end 
f = Foo.new
f + "anything" # "this will work"
f plus "anything" # NoMethodError: undefined method `plus' for main:Object

I'm wondering why calls to operator methods don't require a dot? Or rather, why can't normal methods be called without a dot?

Example

class Foo
  def +(object)
    puts "this will work"
  end
  def plus(object)
    puts "this won't"
  end
end 
f = Foo.new
f + "anything" # "this will work"
f plus "anything" # NoMethodError: undefined method `plus' for main:Object

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

西瑶 2024-10-13 07:32:13

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

当然还有其他选择:

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

    <前><代码>(加 1 2)
    (+ 1 2)

  • Smalltalk 几乎 没有运算符。 Smalltalk 唯一的特殊大小写是仅包含运算符字符的方法不必以冒号结尾。特别是,由于没有运算符,所有方法调用都具有相同的优先级,并且严格从左到右进行计算:2 + 3 * 420,而不是 <代码>14。

    <前><代码>1 加:2
    1+2

  • Scala 几乎没有运算符。就像 Lisp 和 Smalltalk 一样,*-#::: 等只是合法的方法名称。 (实际上,它们也是合法的类、特征、类型和字段名称。)任何方法都可以带点或不带点调用。如果您使用不带点的形式并且该方法只接受一个参数,那么您也可以省略括号。 Scala确实具有优先级,尽管它不是用户可定义的;它仅由名称的第一个字符确定。作为一个额外的变化,以冒号结尾的运算符方法名称是倒置的或右关联的,即 a :: b 相当于 b.::(a) 并且不是a.::(b)

    <前><代码>1.plus(2)
    1 加(2)
    1加2
    1.+(2)
    1 +(2)
    1+2

  • 在 Haskell 中,任何名称由运算符符号组成的函数都被视为运算符。任何函数都可以通过将其括在反引号中而被视为运算符,并且任何运算符都可以通过将其括在方括号中而被视为函数。此外,程序员可以自由定义用户定义运算符的结合性、固定性和优先级。

    <前><代码>加 1 2
    1 `加` 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 上讨论的 Refinement 功能是另一个例子。当前指定的方式将导致不可能优化方法调用和内联方法,即使相关程序实际上并未使用Refinement全部。只需进行简单的调整,它就可以同样具有表现力和强大的功能,并且确保悲观化成本仅发生在实际使用细化s。显然,以这种方式指定的唯一原因是 a) 这种方式更容易原型化,b) YARV 没有优化器,所以甚至没有人费心去思考其中的含义(嗯,除了查尔斯·奥利弗·纳特之外没有人)。

因此,基本上,对于您对 Ruby 设计的任何问题,答案几乎总是“因为 Matz 这么说”或“因为在 1993 年,这种方式更容易实现”。

The answer to this question, as to pretty much every language design question is: "Just because". Language design is a series of mostly subjective trade-offs. And for most of those subjective trade-offs, the only correct answer to the question why something is the way it is, is simply "because Matz said so".

There are certainly other choices:

  • Lisp doesn't have operators at all. +, -, ::, >, = and so on are simply normal legal function names (variable names, actually), just like foo or bar?

    (plus 1 2)
    (+ 1 2)
    
  • Smalltalk almost doesn't have operators. The only special casing Smalltalk has is that methods which consist only of operator characters do not have to end with a colon. In particular, since there are no operators, all method calls have the same precedence and are evaluated strictly left-to-right: 2 + 3 * 4 is 20, not 14.

    1 plus: 2
    1 + 2
    
  • Scala almost doesn't have operators. Just like Lisp and Smalltalk, *, -, #::: and so on are simply legal method names. (Actually, they are also legal class, trait, type and field names.) Any method can be called either with or without a dot. If you use the form without the dot and the method takes only a single argument, then you can leave off the brackets as well. Scala does have precedence, though, although it is not user-definable; it is simply determined by the first character of the name. As an added twist, operator method names that end with a colon are inverted or right-associative, i.e. a :: b is equivalent to b.::(a) and not a.::(b).

    1.plus(2)
    1 plus(2)
    1 plus 2
    1.+(2)
    1 +(2)
    1 + 2
    
  • In Haskell, any function whose name consists of operator symbols is considered an operator. Any function can be treated as an operator by enclosing it in backticks and any operator can be treated as a function by enclosing it in brackets. In addition, the programmer can freely define associativity, fixity and precedence for user-defined operators.

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

There is no particular reason why Ruby couldn't support user-defined operators in a style similar to Scala. There is a reason why Ruby can't support arbitrary methods in operator position, simply because

foo plus bar

is already legal, and thus this would be a backwards-incompatible change.

Another thing to consider is that Ruby wasn't actually fully designed in advance. It was designed through its implementation. Which means that in a lot of places, the implementation is leaking through. For example, there is absolutely no logical reason why

puts(!true)

is legal but

puts(not true)

isn't. The only reason why this is so, is because Matz used an LALR(1) parser to parse a non-LALR(1) language. If he had designed the language first, he would have never picked an LALR(1) parser in the first place, and the expression would be legal.

The Refinement feature currently being discussed on ruby-core is another example. The way it is currently specified, will make it impossible to optimize method calls and inline methods, even if the program in question doesn't actually use Refinements at all. With just a simple tweak, it can be just as expressive and powerful, and ensure that the pessimization cost is only incurred for scopes that actually use Refinements. Apparently, the sole reason why it was specified this way, is that a) it was easier to prototype this way, and b) YARV doesn't have an optimizer, so nobody even bothered to think about the implications (well, nobody except Charles Oliver Nutter).

So, for basically any question you have about Ruby's design, the answer will almost always be either "because Matz said so" or "because in 1993 it was easier to implement that way".

自由如风 2024-10-13 07:32:13

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

相反,Ruby 有一个使用静态定义语法的 Yacc 解析器。您获得了内置运算符,仅此而已。符号出现在语法中的一组固定句子中。正如您所指出的,运算符可以重载,这比大多数语言提供的要重载。

当然这并不是因为Matz懒惰。

Ruby 实际上有一个极其复杂的语法,大致达到了 Yacc 所能完成的极限。要变得更复杂,需要使用可移植性较差的编译器生成器,或者需要用 C 手动编写解析器,而这样做将以自己的方式限制未来的实现可移植性,并且不提供使用 Yacc 输入的世界。这将是一个问题,因为 Ruby 的 Yacc 源代码是唯一的 Ruby 语法文档,因此是“标准”。

The implementation doesn't have the additional complexity that would be needed to allow generic definition of new operators.

Instead, Ruby has a Yacc parser that uses a statically defined grammar. You get the built-in operators and that's it. Symbols occur in a fixed set of sentences in the grammar. As you have noted, the operators can be overloaded, which is more than most languages offer.

Certainly it's not because Matz was lazy.

Ruby actually has a fiendishly complex grammar that is roughly at the limit of what can be accomplished in Yacc. To get more complex would require using a less portable compiler generator or it would have required writing the parser by hand in C, and doing that would have limited future implementation portability in its own way as well as not providing the world with the Yacc input. That would be a problem because Ruby's Yacc source code is the only Ruby grammar documentation and is therefore "the standard".

公布 2024-10-13 07:32:13

因为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

以下是

Because Ruby has "syntax sugar" that allows for a variety of convenient syntax for preset situations. For example:

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

Here's a list of the operator expressions that may be implemented as methods in Ruby.

薔薇婲 2024-10-13 07:32:13

因为 Ruby 的语法设计得与流行的 OO 语言大致相似,并且使用点运算符来调用方法。它借用了对象模型的语言 Smalltalk 没有使用点来表示消息,而且实际上有一种相当“奇怪”的语法,很多人都觉得令人反感。 Ruby 被称为“具有 Algol 语法的 Smalltalk”,其中 Algol 是为我们提供了您在这里讨论的约定的语言。 (当然,实际上除了 Algol 语法之外还有更多差异。)

Because Ruby's syntax was designed to look roughly like popular OO languages, and those use the dot operator to call methods. The language it borrowed its object model from, Smalltalk, didn't use dots for messages, and in fact had a fairly "weird" syntax that many people found off-putting. Ruby has been called "Smalltalk with an Algol syntax," where Algol is the language that gave us the conventions you're talking about here. (Of course, there are actually more differences than just the Algol syntax.)

左耳近心 2024-10-13 07:32:13

缺少大括号对于 ruby​​ 1.8 来说是一些“优势”,但在 ruby​​ 1.9 中,你甚至不能编写 method_0 method_1 some param 它会被拒绝,因此该语言宁愿使用严格版本而不是自由格式。

Missing braces was some "advantage" for ruby 1.8, but with ruby 1.9 you can't even write method_0 method_1 some param it will be rejected, so the language goes rather to the strict version instead of freeforms.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文