我注意到很多处理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
Run Code Online (Sandbox Code Playgroud)
我的问题是&符号背后的功能目的是什么?似乎如果我没有&编写相同的确切代码,它按预期工作:
# 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
Run Code Online (Sandbox Code Playgroud)
Sim*_*tti 36
本文提供了很好的差异概述.
总结一下这篇文章,Ruby允许隐式和显式块.而且,Ruby有block,proc和lambda.
你打电话的时候
def foo(block)
end
Run Code Online (Sandbox Code Playgroud)
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 `<main>'
foo(1, Proc.new { 1 + 1 }, 3)
1
2
3
Run Code Online (Sandbox Code Playgroud)
但是当您&在方法定义中使用&符号时,该块具有不同的含义.您正在明确定义接受块的方法.其他规则将适用(例如每种方法不超过一个块).
def foo(one, two, &block)
p one
p block.call
p two
end
Run Code Online (Sandbox Code Playgroud)
首先,作为一个块,方法签名现在接受"两个参数和一个块",而不是"三个参数".
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 `<main>'
Run Code Online (Sandbox Code Playgroud)
这意味着,您必须强制第三个参数成为使用&符号传递参数的块.
foo(1, 2, &Proc.new { "from the proc" })
1
"from the proc"
2
Run Code Online (Sandbox Code Playgroud)
但是,这是一种非常罕见的语法.在Ruby中,通常使用块来调用带有块的方法{}
foo(1, 2) { "from the block" }
1
"from the block"
2
Run Code Online (Sandbox Code Playgroud)
或do end.
foo(1, 2) do
"from the block"
end
1
"from the block"
2
Run Code Online (Sandbox Code Playgroud)
让我们跳回到方法定义.我之前提到过以下代码是一个显式的块声明.
def foo(one, two, &block)
block.call
end
Run Code Online (Sandbox Code Playgroud)
方法可以隐式接受块.使用隐式块进行调用yield.
def foo(one, two)
p yield
end
foo(1, 2) { "from the block" }
Run Code Online (Sandbox Code Playgroud)
您可以检查块是否通过使用 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"
Run Code Online (Sandbox Code Playgroud)
如果将"块"声明为简单参数(因此没有符号),则这些与块相关的功能将不可用,因为它只是一个无穷大的方法参数.
作为补充,我让自己记住&作为block和之间的转换标志Proc.
要转换block为Proc
def foo(&p)
puts p.class
end
foo {} # => Proc
Run Code Online (Sandbox Code Playgroud)
要转换Proc为ablock
def bar
yield "hello"
end
p = Proc.new {|a| puts a }
bar &p # => hello
Run Code Online (Sandbox Code Playgroud)
好吧,当你有一个block&时,如果你在 block 之前应用,它就会变成Procobject ,反之亦然。
_unary &_:它与块之间的转换有关。如果您不了解这一点,请记住,当您在 Ruby 中看到一元 \xe2\x80\x9c&\xe2\x80\x9d 时,您正在考虑将某个东西变成一个块,或者将一个块变成某个东西。
在第一个示例中,在这一行中shout_n_times(3, &shout),您将变量Proc引用的对象转换为. 然后在方法参数列表中,将其转换回对象。shootblockProc
在您的第二个示例中,它有效,因为您直接传递一个Proc对象作为方法参数,然后调用#call它。