Ruby: 立即调用返回过程的括号表示法

Ruby: bracket notation for immediate call of returned procedure

在Ruby中,当我对返回的Proc进行立即回调时,我通常使用#call:

def multiplier(factor)
  Proc.new { |number| factor * number }
end

puts multiplier(10).call(5) # 50

但我只是 运行 跨过这个括号 shorthand,据我所知,结果相同:

def multiplier(factor)
  Proc.new { |number| factor * number }
end

puts multiplier(10)[5] # 50

我觉得这与 JavaScript 立即调用返回函数非常相似:

const multiplier = (factor) => (number) => factor * number;

console.log(multiplier(10)(5)); // 50

但我以前从未见过语法,而且我似乎无法在任何地方找到它的文档。这只是使用 #call 的语法糖,还是还有更多?而且我似乎找不到任何文档。有什么地方吗?

But I've never seen the syntax before,

我建议你获得更好的教程。大多数教程很早就教授数组和散列,其中解释了这种确切的语法。

and I can't seem to find it documented anywhere.

The documentation for the Proc#[] method is in the documentation of the Proc class,就像所有其他方法一样。

Is this just syntax sugar for using #call

不,它不是 #call 的语法糖。它是 #[].

的语法糖

同样,这个 真的 应该在任何教程的早期就解释过:

foo[bar, baz, quux]

# is syntactic sugar for 

foo.[](bar, baz, quux)

or is there more to it?

不,仅此而已。

And I can't seem to find any doc on it. Is there some somewhere?

foo[bar]foo.[](bar) 的语法糖这一事实通常在有关消息发送的章节或有关运算符的章节中的任何介绍性教程中都有解释,也许也在有关数组的章节中或哈希(因为这是最常见的用例)。

[同样,我很惊讶您遇到过使用 Proc 的高阶编程,但之前从未见过数组或散列,或基本的 Ruby 语法。这似乎是一种非常奇怪的概念教学/学习方式。]

ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification chapter 11.3 Method invocation expressions, subchapter 11.3.1 [=103]中描述的非常清楚 =]一般说明,部分语义,条款b):

An indexing-method-invocation is evaluated as follows:

  1. Evaluate the primary-expression. Let O be the resulting value.
  2. If the indexing-argument-list is present, construct a list of arguments from the indexing-argument-list as described in 11.3.2. Let L be the resulting list.
  3. If the indexing-argument-list is omitted, Create an empty list of arguments L.
  4. Invoke the method [] on O with L as the list of arguments. The value of the indexing-method-invocation is the resulting value.

这里特别感兴趣的是 4) 子句,它解释了语法糖是如何脱糖的,即 O[L] 意味着在 OL 作为参数,或者换句话说 O[L]O.[](L).

的语法糖

The ruby/spec is unfortunately not quite as clear.

And the YARV documentation hides it away in a paragraph without an explicit heading:

Additionally, methods for element reference and assignment may be defined: [] and []= respectively. Both can take one or more arguments, and element reference can take none.

class C
  def [](a, b)
    puts a + b
  end

  def []=(a, b, c)
    puts a * b + c
  end
end

obj = C.new

obj[2, 3]     # prints "5"
obj[2, 3] = 4 # prints "10"

已记录 in the first edition of Programming Ruby by "PragDave" Dave Thomas and Andy Hunt in the chapter on Operator Expressions:

More useful is the fact that classes that you write can participate in operator expressions just as if they were built-in objects. For example, we might want to be able to extract a number of seconds of music from the middle of a song. We could using the indexing operator ```[]`'' to specify the music to be extracted.

class Song
  def [](fromTime, toTime)
    result = Song.new(self.title + " [extract]",
                      self.artist,
                      toTime - fromTime)
    result.setStartTime(fromTime)
    result
  end
end

This code fragment extends class Song with the method ```[]`'', which takes two parameters (a start time and an end time). It returns a new song, with the music clipped to the given interval. We could then play the introduction to a song with code such as:

aSong[0, 0.15].play

the second edition on page 82 中也出现了完全相同的章节,只是方法参数的名称现在遵循标准 Ruby 命名约定。

我目前无法访问第 4 版的纸质副本,但我知道它也在那里。

我也确定它在 The Ruby Programming Language by David Flanagan and Yukihiro "matz" Matsumoto 中有记录,但我手边也没有那个副本。

如果您更喜欢查看源代码,这里是 the source code of Proc#[] in Rubinius,它非常简单:

alias_method :[], :call

同样,这里是the code in TruffleRuby

@CoreMethod(names = { "call", "[]", "yield" }, rest = true, needsBlock = true, alwaysInlined = true)
public abstract static class CallNode extends AlwaysInlinedMethodNode {
    // …
}

the code in JRuby

@JRubyMethod(name = {"call", "[]", "yield", "==="}, rest = true, omit = true)
public final IRubyObject call(ThreadContext context, IRubyObject[] args, Block blockCallArg) {
    // …
}

Is this just syntax sugar for using #call

如上所述,它不是,但知道 #call 的语法糖,即 .() 可能会很有趣。所以,

foo.(bar, baz, quux)

的语法糖
foo.call(bar, baz, quux)