ruby如何解压缩传递给Proc的参数?

Ze *_*Gao 3 ruby arguments argument-unpacking proc

a_proc = Proc.new {|a,b,*c| p c; c.collect {|i| i*b }}
puts a_proc[2,2,4,3]
Run Code Online (Sandbox Code Playgroud)

根据https://ruby-doc.org/core-2.2.0/Proc.html,上面的代码非常直观,a_proc [2,2,4,3]只是a_proc.call(2,2 ,4,3)隐藏“通话”

但是以下内容(效果很好)让我很困惑

a=[2,2,4,3]
puts a_proc.call(a)
puts a_proc.call(*a)
Run Code Online (Sandbox Code Playgroud)

似乎与普通的函数调用有很大不同,因为它不检查传入的数字参数。

但是,按预期方式,如果同样使用参数,则调用语义的方法将引发错误

def foo(a,b,*c)
  c.collect{|i| i*b}
end
foo([1,2,3,4]) #`block in <main>': wrong number of arguments (given 1, expected 2+) (ArgumentError)

foo(*[1,2,3,4]) #works as expected
Run Code Online (Sandbox Code Playgroud)

我不认为这种不一致是设计故障,因此,对此有任何见解将不胜感激。

Jör*_*tag 5

块使用与方法不同的语义来将参数绑定到参数。

在这方面,块语义比赋值语义更类似于赋值语义。实际上,在旧版本的Ruby中,块实际上是用于参数绑定的赋值,您可以编写如下代码:

class Foo; def bar=(val) puts 'setter called!' end end

some_proc = Proc.new {|$foo, @foo, foo.bar|}
some_proc.call(1, 2, 3)
# setter called!
$foo #=> 1
@foo #=> 2
Run Code Online (Sandbox Code Playgroud)

值得庆幸的是,自Ruby 1.9起,情况已不再如此。但是,保留了一些语义:

  • 如果一个块具有多个参数,但仅接收一个参数,则将向该参数发送一条to_ary消息(如果Array该参数尚未存在),并且参数将绑定到参数的元素上。Array
  • 如果块接收的参数多于其参数个数,则它将忽略额外的参数
  • 如果块接收的参数少于其参数的数量,则多余的参数将绑定到 nil

注意:#1是使Hash#each工作如此精美的原因,否则,您将始终必须解构传递给块的数组。

简而言之,块参数的绑定方式与多重分配的绑定方式几乎相同。您可以想象没有赋值器,索引器,全局变量,实例变量和类变量的赋值,只有局部变量,这几乎就是块的参数绑定的工作方式:从块中复制并粘贴参数列表,从,复制并粘贴参数列表yield,放置=中间的一个标志,您就会明白。

现在,您实际上不是在谈论一个街区,而是在谈论一个Proc。对于这一点,你需要知道一些重要的事情:有两个种类的的ProcS,不幸都使用相同的类来实现。(IMO,它们应该是两个不同的类。)一种称为lambda,另一种通常称为proc(令人困惑,因为两者都是Procs)。

无论是在参数绑定和参数传递(即上述赋值语义)方面,还是在行为return(从最接近的词法包围方法返回)方面,过程的行为都像块。

无论是参数绑定和参数传递(即严格的参数检查),还是参数的行为(从lambda本身返回),Lambda的行为都与方法类似return

一个简单的助记符:“ block”和“ proc”押韵,“ method”和“ lambda”都是希腊语。


对您的问题的一句话:

a_proc [2,2,4,3]只是a_proc.call(2,2,4,3)隐藏“调用” 的语法糖

不是语法糖。而是Proc简单地定义与[]行为相同的方法call

什么语法糖:

a_proc.(2, 2, 4, 3)
Run Code Online (Sandbox Code Playgroud)

每次发生

foo.(bar, baz)
Run Code Online (Sandbox Code Playgroud)

被解释为

foo.call(bar, baz)
Run Code Online (Sandbox Code Playgroud)

  • 你能举个例子“如果一个块接收多个参数但只有一个参数,则该参数将绑定到所有参数的数组”?如果你有 `a_proc = proc { |a| a }` 并传递给它 `a_proc.call(1, 2, 3)` 返回值将是 `1`,而不是 `[1, 2, 3]` (2认同)