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将不可用,因为它只是一个无穷大的方法参数。
作为补充,我让自己记住&
作为block
和Proc
之间的转换标志。
将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的块只不过是一种特殊的语法,它允许您将方法作为参数传递给另一个方法。