目的,;(符号)在Ruby中表示进程和调用方法

目的,;(符号)在Ruby中表示进程和调用方法,ruby,proc,Ruby,Proc,我注意到许多处理Ruby进程的示例中都包含以下符号 # 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 我的问题是&符号背后的功能目的是什么?似乎如果我在没有&的情况下编写了相同的代码,

我注意到许多处理Ruby进程的示例中都包含以下符号

# 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
对象,反之亦然

:它与从块到块的转换有关。如果你没有从中得到任何其他东西,请记住,当你在Ruby中看到一个一元“&”时,你是在把某个东西变成一个块,或者把一个块变成某个东西

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

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

提供了对差异的良好概述

总结本文,Ruby允许隐式和显式块。此外,Ruby还有block、proc和lambda

当你打电话的时候

def foo(block)
end
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
首先,作为一个块,方法签名现在接受“两个参数和一个块”,而不是“三个参数”

然而,这是一种非常罕见的语法。在Ruby中,通常使用
{}

foo(1, 2) { "from the block" }
1
"from the block"
2
结束

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\u 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
在hout_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的块只不过是一种特殊的语法,允许您将一个方法作为参数传递给另一个方法

作为补充,我让自己记住
&
作为
过程
之间的转换符号

转换为
Proc

def foo(&p)
  puts p.class
end

foo {} # => Proc
过程
转换为

def bar
  yield "hello"
end
p = Proc.new {|a| puts a }

bar &p # => hello

不要在括号前加空格!:)对不起,我的习惯来自JavaScript世界:)我会在我的例子中纠正它。@wmock,我来自js世界,我不会在parens前面加空格——我不记得有人这样做过。在第一种情况下,你可以调用你的方法
shout n_times(3){shout.call}
shout n_times(3,&shout)
cause完全相同,但不在第二个cause中,因为它正在等待arg!我很清楚,你是说如果我有一个Proc对象并在它前面加上&,结果可以用作方法的代码块吗?另一方面,如果我有一个代码块,然后在它前面加上&,我可以把结果作为一个过程来使用?谢谢只有两个地方可以使用一元前缀
&
运算符:参数列表和参数列表。在参数列表中,它表示“将要传递的块转换为
Proc
,并将其绑定到名称”。在参数列表中,它意味着“将
Proc
转换为一个块,就好像它已作为一个文本块传递一样”,还有一个额外的好处,那就是如果传递的对象不是
Proc
,Ruby将首先调用
到它上面的\u Proc
,将其强制为
Proc
,这使得像
Symbol#to_proc
这样的巧妙技巧成为可能。感谢您的全面响应和示例-真的帮助我更好地理解了这一点!我发现链接的文章令人沮丧,读起来令人不快。谢谢你的精彩替换。谢谢你的示例!真的很有帮助!
   method call    +--start of block specified after the method call
      |           |    
      V           V
shout_n_times(3) do
  puts 'Yolo!'
end
 ^
 |
 +--end of block
def foo(&p)
  puts p.class
end

foo {} # => Proc
def bar
  yield "hello"
end
p = Proc.new {|a| puts a }

bar &p # => hello