Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby:Proc#call vs yield_Ruby_Yield - Fatal编程技术网

Ruby:Proc#call vs yield

Ruby:Proc#call vs yield,ruby,yield,Ruby,Yield,以下两种在Ruby中实现的trice方法在行为上有什么不同 module WithYield def self.thrice 3.times { yield } # yield to the implicit block argument end end module WithProcCall def self.thrice(&block) # & converts implicit block to an explicit, named Pro

以下两种在Ruby中实现的
trice
方法在行为上有什么不同

module WithYield
  def self.thrice
    3.times { yield }      # yield to the implicit block argument
  end
end

module WithProcCall
  def self.thrice(&block)  # & converts implicit block to an explicit, named Proc
    3.times { block.call } # invoke Proc#call
  end
end

WithYield::thrice { puts "Hello world" }
WithProcCall::thrice { puts "Hello world" }

“行为差异”包括错误处理、性能、工具支持等。我认为第一种差异实际上是另一种差异的语法糖分。换句话说,没有行为上的差异

第二种形式允许的是在变量中“保存”块。然后可以在另一个时间点调用该块-回调


嗯。这次我去做了一个快速的基准测试:

require 'benchmark'

class A
  def test
    10.times do
      yield
    end
  end
end

class B
  def test(&block)
    10.times do
      block.call
    end
  end
end

Benchmark.bm do |b|
  b.report do
    a = A.new
    10000.times do
      a.test{ 1 + 1 }
    end
  end

  b.report do
    a = B.new
    10000.times do
      a.test{ 1 + 1 }
    end
  end

  b.report do
    a = A.new
    100000.times do
      a.test{ 1 + 1 }
    end
  end

  b.report do
    a = B.new
    100000.times do
      a.test{ 1 + 1 }
    end
  end

end
结果很有趣:

      user     system      total        real
  0.090000   0.040000   0.130000 (  0.141529)
  0.180000   0.060000   0.240000 (  0.234289)
  0.950000   0.370000   1.320000 (  1.359902)
  1.810000   0.570000   2.380000 (  2.430991)

这表明使用block.call比使用yield

慢近2倍。如果忘记传递块,它们会给出不同的错误消息:

> WithYield::thrice
LocalJumpError: no block given
        from (irb):3:in `thrice'
        from (irb):3:in `times'
        from (irb):3:in `thrice'

> WithProcCall::thrice
NoMethodError: undefined method `call' for nil:NilClass
        from (irb):9:in `thrice'
        from (irb):9:in `times'
        from (irb):9:in `thrice'
但如果您尝试传递“正常”(非块)参数,它们的行为相同:

> WithYield::thrice(42)
ArgumentError: wrong number of arguments (1 for 0)
        from (irb):19:in `thrice'

> WithProcCall::thrice(42)
ArgumentError: wrong number of arguments (1 for 0)
        from (irb):20:in `thrice'

顺便说一句,只需使用以下工具将此更新到当前日期:

ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-linux]
在英特尔i7(1.5年历史)上


还是慢了2倍。有趣。

这里是Ruby 2.x的更新

ruby 2.0.0p247(2013-06-27修订版41674)[x86_64-darwin12.3.0]

我厌倦了手工编写基准测试,所以我创建了一个名为

输出

用户系统总实际值
基准收益率0.930000 0.0000000.930000(0.928682)
板凳呼叫1.650000 0.000000 1.650000(1.652934)
工作台程序0.570000 0.010000 0.580000(0.578605)

我认为最令人惊讶的是,
bench\u收益率
bench\u proc
慢。我希望我能多了解一点为什么会发生这种情况。

其他答案非常全面,涵盖了功能上的差异。我很好奇哪种方法对于那些可以选择接受块的方法来说性能最好,所以我写了一些基准测试(即将开始)。我比较了三种方法:

  • &块内方法签名
  • 使用
    &Proc.new
  • 在另一个块中包装
    产量
代码如下:

require "benchmark"

def always_yield
  yield
end

def sometimes_block(flag, &block)
  if flag && block
    always_yield &block
  end
end

def sometimes_proc_new(flag)
  if flag && block_given?
    always_yield &Proc.new
  end
end

def sometimes_yield(flag)
  if flag && block_given?
    always_yield { yield }
  end
end

a = b = c = 0
n = 1_000_000
Benchmark.bmbm do |x|
  x.report("no &block") do
    n.times do
      sometimes_block(false) { "won't get used" }
    end
  end
  x.report("no Proc.new") do
    n.times do
      sometimes_proc_new(false) { "won't get used" }
    end
  end
  x.report("no yield") do
    n.times do
      sometimes_yield(false) { "won't get used" }
    end
  end

  x.report("&block") do
    n.times do
      sometimes_block(true) { a += 1 }
    end
  end
  x.report("Proc.new") do
    n.times do
      sometimes_proc_new(true) { b += 1 }
    end
  end
  x.report("yield") do
    n.times do
      sometimes_yield(true) { c += 1 }
    end
  end
end
Ruby 2.0.0p247和1.9.3p392的性能相似。以下是1.9.3的结果:

                  user     system      total        real
no &block     0.580000   0.030000   0.610000 (  0.609523)
no Proc.new   0.080000   0.000000   0.080000 (  0.076817)
no yield      0.070000   0.000000   0.070000 (  0.077191)
&block        0.660000   0.030000   0.690000 (  0.689446)
Proc.new      0.820000   0.030000   0.850000 (  0.849887)
yield         0.250000   0.000000   0.250000 (  0.249116)
当不总是使用显式
&block
参数时,添加该参数确实会降低方法的速度。如果该块是可选的,则不要将其添加到方法签名中。而且,对于传递块,在另一个块中包装
yield
是最快的


也就是说,这些是一百万次迭代的结果,所以不要太担心。如果有一种方法以百万分之一秒为代价使代码更清晰,那么还是使用它吧。

我发现结果会有所不同,这取决于您是否强制Ruby构造块(例如,一个预先存在的过程)

给出了结果:

Ruby 2.3.1 at 2016-11-15 23:55:38 +1300

Warming up --------------------------------------
          block.call   266.502k i/100ms
    yield with block   269.487k i/100ms
               yield   262.597k i/100ms
Calculating -------------------------------------
          block.call      8.271M (± 5.4%) i/s -     41.308M in   5.009898s
    yield with block     11.754M (± 4.8%) i/s -     58.748M in   5.011017s
               yield     16.206M (± 5.6%) i/s -     80.880M in   5.008679s

Comparison:
               yield: 16206091.2 i/s
    yield with block: 11753521.0 i/s - 1.38x  slower
          block.call:  8271283.9 i/s - 1.96x  slower

如果将
do\u-call(&existing\u-block)
更改为
do\u-call{}
,您会发现在这两种情况下,它的速度都要慢5倍左右。我认为这样做的原因应该是显而易见的(因为Ruby被迫为每次调用构造一个Proc)。

如果这是真的,我认为Ruby会更加一致(即如果
yield
只是
Proc\call
的语法糖),但我不认为这是真的。e、 g.存在不同的错误处理行为(见下面我的答案)。我还看到有人建议(例如),
yield
效率更高,因为它不必先创建一个
Proc
对象,然后调用其
call
方法,在MRI 1.8.6p114上。在JRuby(1.3.0,JVM1.6.0_16服务器虚拟机)上,差异更为显著:
Proc\35; call
的速度大约是
yield
的8倍。也就是说,JRuby上的
yield
速度是MRI上的
yield
速度的两倍。我在MRI 1.8.7p174 x86_64-linux上做了我的工作。您还缺少第三种情况:
def测试(&block);10.次数和块数;结束
,测试结果应与收益率情况相同<代码>块。调用比
yield
慢约1.7倍。我相信这是因为在
bench\u proc
中,一元运算符实际上是将proc转换为
调用的块,跳过
bench\u yield
bench\u调用
中的
的块创建开销。这是一种奇怪的特殊情况使用,看起来在大多数情况下,
yield
的速度更快。有关proc到块赋值的更多信息:(部分:一元数&)
Integer#次
调用
yield
(c版本,rb_yield,采用表示块的值)。这就是为什么bench_proc这么快。宝石怎么了?不同类型的ruby闭包之间的行为差异旁注:
def-three(&block)
更加自我记录,特别是与隐藏在大型方法中的
yield
相比。关于cldwalker提供的链接的说明……这是错误的。可以将多个块(即闭包)传递给一个方法。(对于提到“编译”Ruby的人来说,也很难认真对待。)传递多个块时,你会得到同样方便的语法糖吗?不,你能做到吗?是的,很容易。
                  user     system      total        real
no &block     0.580000   0.030000   0.610000 (  0.609523)
no Proc.new   0.080000   0.000000   0.080000 (  0.076817)
no yield      0.070000   0.000000   0.070000 (  0.077191)
&block        0.660000   0.030000   0.690000 (  0.689446)
Proc.new      0.820000   0.030000   0.850000 (  0.849887)
yield         0.250000   0.000000   0.250000 (  0.249116)
require 'benchmark/ips'

puts "Ruby #{RUBY_VERSION} at #{Time.now}"
puts

firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'

def do_call(&block)
    block.call
end

def do_yield(&block)
    yield
end

def do_yield_without_block
    yield
end

existing_block = proc{}

Benchmark.ips do |x|
    x.report("block.call") do |i|
        buffer = String.new

        while (i -= 1) > 0
            do_call(&existing_block)
        end
    end

    x.report("yield with block") do |i|
        buffer = String.new

        while (i -= 1) > 0
            do_yield(&existing_block)
        end
    end

    x.report("yield") do |i|
        buffer = String.new

        while (i -= 1) > 0
            do_yield_without_block(&existing_block)
        end
    end

    x.compare!
end
Ruby 2.3.1 at 2016-11-15 23:55:38 +1300

Warming up --------------------------------------
          block.call   266.502k i/100ms
    yield with block   269.487k i/100ms
               yield   262.597k i/100ms
Calculating -------------------------------------
          block.call      8.271M (± 5.4%) i/s -     41.308M in   5.009898s
    yield with block     11.754M (± 4.8%) i/s -     58.748M in   5.011017s
               yield     16.206M (± 5.6%) i/s -     80.880M in   5.008679s

Comparison:
               yield: 16206091.2 i/s
    yield with block: 11753521.0 i/s - 1.38x  slower
          block.call:  8271283.9 i/s - 1.96x  slower