Ruby 中 &(&符号)的用途用于过程和调用方法
Purpose of & (ampersand) in Ruby for procs and calling methods
我注意到很多处理 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
我的问题是 & 符号背后的功能目的是什么?似乎如果我不带 & 编写完全相同的代码,它会按预期工作:
# 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
好吧,当你有一个 块 时,如果你在块之前应用 &
,它就会变成 Proc
对象,反之亦然。
_unary &_
:跟方块之间的东西转换有关。如果您对此一无所知,请记住,当您在 Ruby 中看到一元“&”时,您正在考虑将某物变成一个块,或者将一个块变成某物。
在您的第一个示例中,在这一行 shout_n_times(3, &shout)
中,您将 shoot
变量引用的 Proc
对象转换为 block
。然后在方法参数列表中,将其转换回 Proc
对象。
在你的第二个例子中它起作用了,因为你直接传递了一个 Proc
对象作为方法参数,然后在它上面调用 #call
。
This article 很好地概述了差异。
总而言之,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 `<main>'
foo(1, Proc.new { 1 + 1 }, 3)
1
2
3
但是当您在方法定义中使用与号 &
时,该块具有不同的含义。您正在 明确地 定义接受块的方法。其他规则将适用(例如每个方法不超过一个块)。
def foo(one, two, &block)
p one
p block.call
p two
end
首先,作为一个块,方法签名现在接受"two parameters and a block",而不是"three parameters"。
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>'
这意味着,您必须强制第三个参数成为一个块,通过与号传递参数。
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?
检查块是否已通过
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"
如果您将 "block" 声明为简单参数(因此没有&符号),这些与块相关的功能将不可用,因为它只是一个匿名方法参数。
区别在于你的第一个例子:
# 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
| |
V V
shout_n_times(3) do
puts 'Yolo!'
end
^
|
+--end of block
你看,块就像一个方法,块在方法调用中作为不可见参数传递,之后块被写入。在方法定义内部,编写方法定义的人可以使用 yield() 执行块。 Ruby 的块只不过是一种特殊的语法,它允许您将一个方法作为参数传递给另一个方法。
作为补充,我让自己记住&
作为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
我注意到很多处理 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
我的问题是 & 符号背后的功能目的是什么?似乎如果我不带 & 编写完全相同的代码,它会按预期工作:
# 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
好吧,当你有一个 块 时,如果你在块之前应用 &
,它就会变成 Proc
对象,反之亦然。
_unary &_
:跟方块之间的东西转换有关。如果您对此一无所知,请记住,当您在 Ruby 中看到一元“&”时,您正在考虑将某物变成一个块,或者将一个块变成某物。
在您的第一个示例中,在这一行 shout_n_times(3, &shout)
中,您将 shoot
变量引用的 Proc
对象转换为 block
。然后在方法参数列表中,将其转换回 Proc
对象。
在你的第二个例子中它起作用了,因为你直接传递了一个 Proc
对象作为方法参数,然后在它上面调用 #call
。
This article 很好地概述了差异。
总而言之,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 `<main>'
foo(1, Proc.new { 1 + 1 }, 3)
1
2
3
但是当您在方法定义中使用与号 &
时,该块具有不同的含义。您正在 明确地 定义接受块的方法。其他规则将适用(例如每个方法不超过一个块)。
def foo(one, two, &block)
p one
p block.call
p two
end
首先,作为一个块,方法签名现在接受"two parameters and a block",而不是"three parameters"。
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>'
这意味着,您必须强制第三个参数成为一个块,通过与号传递参数。
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?
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"
如果您将 "block" 声明为简单参数(因此没有&符号),这些与块相关的功能将不可用,因为它只是一个匿名方法参数。
区别在于你的第一个例子:
# 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
| |
V V
shout_n_times(3) do
puts 'Yolo!'
end
^
|
+--end of block
你看,块就像一个方法,块在方法调用中作为不可见参数传递,之后块被写入。在方法定义内部,编写方法定义的人可以使用 yield() 执行块。 Ruby 的块只不过是一种特殊的语法,它允许您将一个方法作为参数传递给另一个方法。
作为补充,我让自己记住&
作为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