【问题标题】:Passing block from one method to another将块从一种方法传递到另一种方法
【发布时间】:2016-04-20 10:28:11
【问题描述】:

我需要将一个块从一种方法传递给另一种方法(我想调用Rails.cache.fetch,并将块传递给我的方法)。

我可以将&block 添加到参数列表并使用它来将其传递给下一个方法,或者我可以创建一个新块并在其中调用yield。我写了一个简短的例子和基准:

require "benchmark"

def with_block(&block)
  do_something 'Test', &block
end

def with_yield
  do_something('Test') { yield }
end

def do_something(string)
  "#{yield} #{string}"
end

n = 5_000_000
Benchmark.bmbm do |x|
  x.report("&block") do
    n.times { with_block { "Yo" } }
  end
  x.report("yield") do
    n.times { with_yield { "Yo" } }
  end
end


&block   3.320000   0.010000   3.330000 (  3.340438)
yield    1.670000   0.000000   1.670000 (  1.669504)
--------------------------------- total: 5.000000sec

             user     system      total        real
&block   3.270000   0.010000   3.280000 (  3.275914)
yield    1.680000   0.000000   1.680000 (  1.682768)

看起来{ yield } 方法要快得多。这是正确的方法吗?由于在新创建的块中调用yield,是否有任何我不知道的问题?

【问题讨论】:

  • 您拥有Proc,以及您也可以使用的专用Proc,即lambda。方法也是一个块,但最初不是一个对象。您可以将方法转换为Method 对象。您有更多的选择,而不仅仅是通过块的两种方式。所以我认为你的问题有些不完整。传递匿名块没有特殊考虑,除了不按名称访问它(即在方法之外),尽管我认为 Procs 和 lambdas 都被认为是“匿名的”。
  • yield 确实更快。正是因为它的功能较少。您只能调用传递的块。例如,您不能将其传递给下一个方法。顺便说一句,您的测试没有意义。 do_something 不使用该块。
  • @SergioTulentsev 你可以通过调用它来传递它。 class Array; def my_map; map{|e| yield(e)} end end.
  • @sawa:这没有通过。这将创建一个完全不相关的块,其中包含所有产生的(语法)开销。与def my_map(&block); map(&block); end 比较。
  • @sawa:因此,现在中间方法被底层方法的实现细节(其块签名)污染了。

标签: ruby proc


【解决方案1】:

简短回答:始终使用yield,除非您有充分的理由明确引用&block

见:Why blocks make ruby methods 439% slower

使用&block,您将获得一个具体的Proc,您可以在其上执行各种stuff 并且可以四处移动。但是,对于 yield 和隐式块,您只能调用该块。

通过使用yield,解释器可以绕过所有的Proc具体化,因为它知道开发人员将无法使用它;因此它可以只保留 C 级结构,而不必设置 Ruby 级对象。

【讨论】:

  • 我理解为什么 yield 比明确引用 &block 更快。是否有更好的方法将块从一种方法传递到另一种方法,期望从新块调用yield
  • 如果您有充分的理由接受性能打击,那么您可以明确引用&block。 (然后您只需使用block.call,而不是yield。)或者,您可以考虑使用的另一个技巧是Kernel#block_given?apidock.com/ruby/Kernel/block_given%3F——可以在没有明确的&block 声明的情况下使用。
猜你喜欢
  • 1970-01-01
  • 2013-01-29
  • 1970-01-01
  • 1970-01-01
  • 2014-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
相关资源
最近更新 更多