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

Ruby 何时使用方法{}vs方法(&;block)?,ruby,Ruby,当我们希望将块传递给方法时,我们希望何时执行: block = Proc.new { puts 'test blocks & procs' } def method(&block) yield end VS 是否有任何特殊情况需要使用其中一种?使用Procs可以避免重复。比较一下: arr1 = [1,2,3] arr2 = [4,5,6] 使用块,可以重复块两次: arr1.map { |n| n * 2 } arr2.map { |n| n * 2 } 使用Proc

当我们希望将块传递给方法时,我们希望何时执行:

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


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

使用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)

使用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) 块不是对象,因此不能在变量中捕获块,也不能显式地将块传递给方法。但是,您可以使用
&
运算符将块转换为过程实例,过程实例是一个可以指定给变量并传递给方法的对象

2)
Proc.new()。因此,命名变量块是一种误导

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
与之相比:

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) 何时要使用
块并产生
v<代码>&阻止并调用

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

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
我建议您按照以下两条规则操作:

  • 当您编写一个需要块的方法时,总是使用
    yield
    执行块
  • 如果Li 1不能为您工作,请考虑捕获块并使用<代码>调用< /代码>(或<代码> []/COD>) 1) 块不是对象,因此不能在变量中捕获块,也不能显式地将块传递给方法。但是,您可以使用
    &
    运算符将块转换为过程实例,过程实例是一个可以指定给变量并传递给方法的对象

    2)
    Proc.new()。因此,命名变量块是一种误导

    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
    
    与之相比:

    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) 何时要使用
    块并产生
    v<代码>&阻止并调用

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

    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
    
    我建议您按照以下两条规则操作:

  • 当您编写一个需要块的方法时,总是使用
    yield
    执行块
  • 如果Li 1不能为您工作,请考虑捕获块并使用<代码>调用< /代码>(或<代码> []/COD>)
    基于以下基准,Block似乎要快一点:

    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
    
    2.00GHz下i7-4510U CPU上的基准测试结果


    基于以下基准,Block似乎要快一点:

    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
    
    2.00GHz下i7-4510U CPU上的基准测试结果


    你的第一个例子实际上没有任何作用。。。(从未运行过
    put
    )您的第一个示例不应包含
    yield
    ,而应包含
    block.call
    。目前,该方法忽略传入的
    变量,这就是为什么不输出任何内容。。。(从未运行过
    put
    )您的第一个示例不应包含
    yield
    ,而应包含
    block.call
    。目前,该方法忽略传入的
    变量,这就是不输出任何内容的原因。