Ruby中的&(和号)用于procs和调用方法的目的

wmo*_*ock 20 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
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)

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


ify*_*ndy 8

作为补充,我让自己记住&作为block和之间的转换标志Proc.

要转换blockProc

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)


Aru*_*hit 5

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

\n\n

_unary &_:它与块之间的转换有关。如果您不了解这一点,请记住,当您在 Ruby 中看到一元 \xe2\x80\x9c&\xe2\x80\x9d 时,您正在考虑将某个东西变成一个块,或者将一个块变成某个东西。

\n\n

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

\n\n

在您的第二个示例中,它有效,因为您直接传递一个Proc对象作为方法参数,然后调用#call它。

\n

  • 只有两个地方可以使用一元前缀 `&amp;` 运算符:参数列表和形参列表。在参数列表中,它的意思是“转换传递给 `Proc` 的块并将其绑定到名称”。在参数列表中,它的意思是“将 `Proc` 转换为块,就好像它已作为文字块传递一样”,还有一个额外的好处,如果传递的对象不是 `Proc`,Ruby 将首先调用 ` to_proc` 将其强制为 `Proc`,这使得诸如 `Symbol#to_proc` 之类的巧妙技巧成为可能。 (3认同)