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

时间:2014-09-04 18:22:09

标签: ruby

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

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

VS

def method
  yield
end

method { puts 'test blocks & procs' }

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

3 个答案:

答案 0 :(得分:3)

使用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 :(得分:1)

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 `<main>'

比较:

def do_stuff(arg)
  puts arg
  yield
end

do_stuff(10) {puts "I'm a block"}

--output:--
10
I'm a block

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

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(或[]

答案 2 :(得分:0)

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

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.700k i/100ms
            Proc   144.151k i/100ms
           Block      4.786M (± 1.6%) i/s -     23.952M
            Proc      4.269M (± 2.3%) i/s -     21.334M