首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Ruby: Proc#call vs yield

Ruby: Proc#call vs yield
EN

Stack Overflow用户
提问于 2009-09-11 10:25:37
回答 3查看 42.3K关注 0票数 81

下面两个thrice方法的Ruby语言实现在行为上有什么不同?

代码语言:javascript
复制
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" }

我所说的“行为差异”包括错误处理、性能、工具支持等。

EN

回答 3

Stack Overflow用户

发布于 2009-09-11 10:31:36

如果您忘记传递块,它们会给出不同的错误消息:

代码语言:javascript
复制
> 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'

但是,如果您尝试传递一个“正常”(非块)参数,它们的行为是相同的:

代码语言:javascript
复制
> 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'
票数 7
EN

Stack Overflow用户

发布于 2016-11-15 18:58:23

我发现结果是不同的,这取决于你是否强制Ruby构造块(例如,一个预先存在的proc)。

代码语言:javascript
复制
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

给出了结果:

代码语言:javascript
复制
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_call(&existing_block)改成do_call{},你会发现在这两种情况下都要慢5倍。我认为这样做的原因应该是显而易见的(因为Ruby被迫为每个调用构造一个Proc )。

票数 2
EN

Stack Overflow用户

发布于 2011-05-04 12:51:36

顺便说一句,只需使用以下命令将其更新到当前日期:

代码语言:javascript
复制
ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-linux]

在英特尔i7 (1.5年前)上。

代码语言:javascript
复制
user     system      total        real
0.010000   0.000000   0.010000 (  0.015555)
0.030000   0.000000   0.030000 (  0.024416)
0.120000   0.000000   0.120000 (  0.121450)
0.240000   0.000000   0.240000 (  0.239760)

还是慢了2倍。有意思的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1410160

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档