Ruby 中 &(& 符号)用于 procs 和调用方法的目的
Posted
技术标签:
【中文标题】Ruby 中 &(& 符号)用于 procs 和调用方法的目的【英文标题】:Purpose of & (ampersand) in Ruby for procs and calling methods 【发布时间】:2015-04-10 23:04:11 【问题描述】:我注意到很多处理 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
【问题讨论】:
括号前不要加空格! :) 对不起,我的习惯来自 javascript 世界 :) 我会在我的例子中纠正它。 ablogaboutcode.com/2012/01/04/the-ampersand-operator-in-ruby @wmock,我来自 js 世界,我不会在括号前放空格——我不记得看到其他人这样做了。 在第一种情况下,您可以调用您的方法shout_n_times(3) shout.call
或 shout_n_times(3, &shout)
因为完全相同,但在第二种情况下则不然,因为正在等待 arg!
【参考方案1】:
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
首先,作为一个块,方法签名现在接受“两个参数和一个块”,而不是“三个参数”。
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"
如果您将“块”声明为简单参数(因此没有与号),这些与块相关的功能将不可用,因为它只是一个匿名方法参数。
【讨论】:
感谢您的详尽回复和示例 - 真的帮助我更好地理解这一点! 我发现链接的文章令人沮丧且阅读起来不愉快。感谢您的出色替换。【参考方案2】:作为补充,我让自己记住&
作为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
【讨论】:
这是我见过的最好的解释。谢谢!【参考方案3】:好吧,当你有一个块,如果你在块之前应用&
,它就会变成Proc
对象,反之亦然。
_unary &_
:它与将事物与块之间的转换有关。如果您对此没有采取任何其他措施,请记住,当您在 Ruby 中看到一元“&”时,您正在考虑将某物变成一个块,或者将一个块变成某物。
在您的第一个示例中,在这一行shout_n_times(3, &shout)
中,您将shoot
变量引用的Proc
对象转换为block
。然后在方法参数列表中,将其转换回Proc
对象。
在您的第二个示例中它有效,因为您直接将 Proc
对象作为方法参数传递,然后在其上调用 #call
。
【讨论】:
我很清楚,你是说如果我有一个 Proc 对象并在它前面加上 &,结果可以用作方法的代码块?另一方面,如果我有一个代码块,然后在它前面加上 &,我可以将结果用作 proc 吗?谢谢! 只有两个地方可以使用一元前缀&
运算符:参数列表和参数列表。在参数列表中,它的意思是“将要传递的块转换为Proc
并将其绑定到名称”。在参数列表中,它的意思是“将Proc
转换为块,就好像它已作为文字块传递一样”,另外还有一个好处是,如果传递的对象不是Proc
,Ruby 将首先调用@ 987654336@ 将其强制为Proc
,这使得Symbol#to_proc
等巧妙的技巧成为可能。【参考方案4】:
不同之处在于您的第一个示例:
# 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 的块只不过是一种特殊的语法,它允许您将一个方法作为参数传递给另一个方法。
【讨论】:
感谢示例!真的很有帮助!以上是关于Ruby 中 &(& 符号)用于 procs 和调用方法的目的的主要内容,如果未能解决你的问题,请参考以下文章
&: 在红宝石中是啥意思,它是一个混合了符号的块吗? [复制]
雷林鹏分享:Ruby 日期 & 时间(Date & Time)
符号 &tagname 的含义:用于材质 UI 中的 css 样式。例如:&div: display: 'flex' '