Ruby中用于proc和调用方法的&(&符号)的用途

我注意到很多处理Ruby Procs的例子都有以下&符号。

# Ruby Example shout = Proc.new { puts 'Yolo!' } def shout_n_times(n, &callback) n.times do callback.call end end shout_n_times(3, &shout) # prints 'Yolo!' 3 times 

我的问题是&符号背后的function目的是什么? 似乎如果我在没有&的情况下编写相同的确切代码,它会按预期工作:

 # Same code as previous without & shout = Proc.new { puts 'Yolo!' } def shout_n_times(n, callback) n.times do callback.call end end shout_n_times(3, shout) # prints 'Yolo!' 3 times 

本文提供了很好的差异概述。

总结一下这篇文章,Ruby允许隐式和显式块。 而且,Ruby有block,proc和lambda。

你打电话的时候

 def foo(block) end 

block只是该方法的一个简单参数。 参数在变量block引用,您与它的交互方式取决于您传递的对象的类型。

 def foo(one, block, two) p one p block.call p two end foo(1, 2, 3) 1 NoMethodError: undefined method `call' for 2:Fixnum from (irb):3:in `foo' from (irb):6 from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `
' foo(1, Proc.new { 1 + 1 }, 3) 1 2 3

但是当您在方法定义中使用&符号时,该块具有不同的含义。 您正在明确定义接受块的方法。 其他规则将适用(例如每种方法不超过一个块)。

 def foo(one, two, &block) p one p block.call p two end 

首先,作为一个块,方法签名现在接受“两个参数和一个块”,而不是“三个参数”。

 foo(1, 2, Proc.new { "from the proc" }) ArgumentError: wrong number of arguments (3 for 2) from (irb):7:in `foo' from (irb):12 from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `
'

这意味着,您必须强制第三个参数成为使用&符号传递参数的块。

 foo(1, 2, &Proc.new { "from the proc" }) 1 "from the proc" 2 

但是,这是一种非常罕见的语法。 在Ruby中,带有块的方法通常使用{}调用

 foo(1, 2) { "from the block" } 1 "from the block" 2 

或者do end

 foo(1, 2) do "from the block" end 1 "from the block" 2 

让我们跳回到方法定义。 我之前提到过以下代码是一个显式的块声明

 def foo(one, two, &block) block.call end 

方法可以隐式接受块。 使用yield调用隐式块。

 def foo(one, two) p yield end foo(1, 2) { "from the block" } 

您可以使用block_given?检查块是否通过block_given?

 def foo(one, two) if block_given? p yield else p "No block given" end end foo(1, 2) { "from the block" } => "from the block" foo(1, 2) => "No block given" 

如果将“块”声明为简单参数(因此没有符号),则这些与块相关的function将不可用,因为它只是一个无穷大的方法参数。

作为补充,我让自己记住&作为blockProc之间的转换标志。

block转换为Proc

 def foo(&p) puts p.class end foo {} # => Proc 

Proc转换为block

 def bar yield "hello" end p = Proc.new {|a| puts a } bar &p # => hello 

好吧,当你有一个块时 ,如果你在块之前应用& ,它就会变成Proc对象,反之亦然。

_unary &_ :它与块之间的转换有关。 如果你不采取任何其他措施,请记住,当你在Ruby中看到一元“&”时,你正在考虑将某些东西变成块,或者阻塞某些东西。

在您的第一个示例中,在此行shout_n_times(3, &shout) ,您将由shoot变量引用的Proc对象转换为block 。 然后在方法参数列表中,将其转换回Proc对象。

在你的第二个例子中它可以工作,因为你直接传递一个Proc对象作为方法参数,然后在它上面调用#call

区别在于你的第一个例子:

 # Ruby Example shout = Proc.new { puts 'Yolo!' } def shout_n_times(n, &callback) n.times do callback.call end end shout_n_times(3, &shout) 

…您的方法调用语法允许您重写方法定义,如下所示:

 shout = Proc.new { puts 'Yolo!' } def shout_n_times(n) n.times do yield end end shout_n_times(3, &shout) --output:-- Yolo! Yolo! Yolo! 

这两个陈述:

 shout = Proc.new { puts 'Yolo!' } ... shout_n_times(3, &shout) 

……相当于:

 shout_n_times(3) do puts 'Yolo!' end 

在shout_n_times()的方法定义中编写yield()会调用方法调用之后指定的块:

  method call +--start of block specified after the method call | | VV shout_n_times(3) do puts 'Yolo!' end ^ | +--end of block 

你看,一个块就像一个方法,一个块作为一个不可见的参数传递给方法调用,然后写入块。 在方法定义中,编写方法定义的人可以使用yield()执行块。 Ruby的块只不过是一种特殊的语法,它允许您将方法作为参数传递给另一个方法。