何时使用方法{} vs方法(&block)?

当我们想要将块传递给方法时,我们什么时候该做:

block = Proc.new { puts 'test blocks & procs' } def method(&block) yield end 

VS

 def method yield end method { puts 'test blocks & procs' } 

是否有任何特殊情况我们想要使用其中一种?

使用Procs让你不要重复自己。 比较一下:

 arr1 = [1,2,3] arr2 = [4,5,6] 

使用块,重复两次块:

 arr1.map { |n| n * 2 } arr2.map { |n| n * 2 } 

使用Procs时,您可以重用该对象:

 multiply_2 = Proc.new do |n| n * 2 end arr1.map(&multiply_2) arr2.map(&multiply_2) 

1)块不是对象,因此无法在变量中捕获块,并且不能将块显式传递给方法。 但是,您可以使用&运算符将块转换为Proc实例,而Proc实例是可以分配给变量并传递给方法的对象。

2) Proc.new()不返回块 – 它返回一个Proc实例。 因此,命名变量块会产生误导。

3) yield只调用一个块,这是方法调用后指定的东西:

 do_stuff(10) {puts 'hello'} #<-- block do_stuf(10) do |x| #<--'do' marks the start of a block puts x + 2 end #<--end of block block = Proc.new {puts 'hello'} ^ | +--- #not a block 

yield不会调用碰巧作为参数传递给方法的Proc实例:

 def do_stuff(arg) yield end p = Proc.new { puts 'test blocks & procs' } do_stuff(p) --output:-- 1.rb:2:in `do_stuff': no block given (yield) (LocalJumpError) from 1.rb:6:in `
'

相比于:

 def do_stuff(arg) puts arg yield end do_stuff(10) {puts "I'm a block"} --output:-- 10 I'm a block 

4)您可以将块转换为Proc实例并使用&将其捕获到变量中,如下所示:

 def do_stuff(arg, &p) puts arg p.call end do_stuff(10) {puts "I'm a block"} --output:-- 10 I'm a block 

说谎者! 你不再是一个街区了! 通常,用于捕获块的变量名称写为&block

 def do_stuff(arg, &block) puts arg block.call end 

但这在技术上是不正确的; block变量将包含一个Proc实例,如下所示:

 def do_stuff(arg, &block) puts arg puts block.class end do_stuff(10) {puts "I'm a block"} --output:-- 10 Proc 

5)您也可以使用&运算符将Proc实例转换为块,正如Nobita的答案所示:

 def do_stuff(x, y) yield(x, y) end p = Proc.new {|x, y| puts x+y} do_stuff(10, 20, &p) --output:-- 30 

在该示例中,方法调用do_stuff(10, 20, &p)等同于编写:

 do_stuff(10, 20) {|x, y| puts x+y} 

6)你想什么时候使用一个block and yield v。 &block and call

捕获变量中的块的一个用例是,您可以将其传递给另一个方法:

 def do_stuff(arg, &a_proc) result = arg * 2 do_other_stuff(result, a_proc) end def do_other_stuff(x, p) 1.upto(x) do |i| p[i] #Proc#[] is a synonym for Proc#call end end do_stuff(2) {|x| puts x} --output:-- 1 2 3 4 

我建议你遵守这两条规则:

  1. 当您编写一个需要块的方法时,请始终执行带有yield的块。
  2. 如果#1不适合您,请考虑捕获块并使用call (或[]

基于以下基准测试,块似乎更快一些:

 require 'benchmark/ips' prc = Proc.new { '' } def _proc(&block) yield end def _block yield end Benchmark.ips do |x| x.report('Block') { _block { '' } } x.report('Proc') { _proc(&prc) } end 

在i7-4510U CPU @ 2.00GHz上获得基准测试结果

  Block 149.700ki/100ms Proc 144.151ki/100ms Block 4.786M (± 1.6%) i/s - 23.952M Proc 4.269M (± 2.3%) i/s - 21.334M