帮助理解Ruby中的yield和枚举器

时间:2009-06-14 15:32:11

标签: ruby enumerable

如果有人能帮我理解在枚举器中使用Yielder与在枚举器中调用yield之间的区别,我将不胜感激。

“有条不紊的Rubyist”表明,人们不会“从块中屈服”,但并不能准确解释发生了什么。

由于

3 个答案:

答案 0 :(得分:5)

如果您首先了解收益率如何运作,这可能会有所帮助。这是一个例子:

def do_stuff
  if block_given?
    yield 5
  else
    5
  end
end

result = do_stuff {|x| x * 3 }
puts result

--output:--
15

在do_stuff方法中调用:

do_stuff {|x| x * 3 }

..块就像一个函数,并传递给方法do_stuff。在do_stuff中,yield调用函数并传递指定的参数 - 在本例中为5。

需要注意的一些重要事项:

  1. yield在方法

  2. 中称为
  3. 调用方法时,可以将块传递给方法

  4. yield用于调用块。

  5. 好的,现在让我们来看看你的评论问题:

      

    是真的吗?
    e = Enumerator.new do |y| 
      y << 1 
      y << 2 
      y << 3 
    end 
    
         

    完全相同
    e = Enumerator.new do   #I think you forgot to write .new here
        yield 1 
        yield 2 
        yield 3 
    end
    

    在第二个示例中,任何地方都没有方法定义 - 因此您无法调用yield。错误!因此,这两个例子并不相同。

    但是,你可以这样做:

    def do_stuff
      e = Enumerator.new do 
          yield 1 
          yield 2 
          yield 3 
      end 
    end
    
    my_enum = do_stuff {|x| puts x*3}
    my_enum.next
    
    --output:--
    3
    6
    9
    1.rb:12:in `next': iteration reached an end (StopIteration)
        from 1.rb:12:in `<main>'
    

    但这是一个有趣的枚举器,因为它不会产生任何值 - 它只是执行一些代码(恰好打印一些输出),然后结束。该枚举器几乎等同于:

    def do_stuff
      e = Enumerator.new do 
      end 
    end
    
    my_enum = do_stuff
    my_enum.next
    
    --output:--
    1.rb:7:in `next': iteration reached an end (StopIteration)
        from 1.rb:7:in `<main>'
    

    当枚举器无法生成值时,会引发StopIteration异常。因此,在这两种情况下,枚举器都无法生成值。

      

    但我仍然不清楚“yielder”在做什么。它看起来   就像它正在收集所有计算值,以便它可以   当您使用枚举器时,他们会在后来反刍。如果那是   案件,那么它似乎只适用于“小”   序列....你不想让一个存储50的枚举器   百万件物品。

    没有。实际上,您可以创建一个生成无限数量值的枚举器。这是一个例子:

    e = Enumerator.new do |y|
      val = 1
    
      while true
        y << val
        val += 1
      end
    
    end
    
    puts e.next
    puts e.next
    puts e.next
    
    --output:--
    1
    2
    3
    

    添加一些调试消息应该具有洞察力:

    e = Enumerator.new do |y|
      val = 1
    
      while true
        puts "in while loop"
        y << val
        val += 1
      end
    
    end
    
    puts e.next
    
    --output:--
    in while loop
    1
    

    请注意,邮件只打印一次。所以事情正在发生并不明显:

    e = Enumerator.new do |y|
      val = 1
    
      while true
        puts "in while loop"
        y << val
        puts "just executed y << val"
        val += 1
      end
    
    end
    
    puts e.next
    
    --output:--
    in while loop
    1
    

    由于消息“刚执行y&lt;&lt; val”未显示在输出中,这意味着执行必须在行y << val上停止。因此,枚举器没有连续旋转while循环并将所有值都插入到y中 - 即使语法与将值推入数组完全相同:arr << val

    y << val的真正含义是:当调用e.next()时产生此值,然后继续执行下一行。如果您在上一个示例中添加另一个e.next,您将看到此额外输出:

    just executed y << val
    in while loop
    2
    

    正在发生的事情是,在代码中遇到y << val时,执行始终会停止。然后调用e.next在右侧生成值,然后在下一行继续执行。

    如果ruby为这样的yielder语句创建了语法,那可能会更有意义:

    y >> val
    

    我们可以将其解释为含义:暂停执行,然后调用e.next时生成val。

    David Black建议不要使用y.yield val语法,这相当于y << val,以免读者认为它与yield语句的工作方式类似。 y.yield val应该被解释为:“在这里停止执行,当下一个被调用时生成val,然后在下一行继续执行。就我个人而言,我认为语法y << val比{{1}更突出因此,更容易在代码中找到并轻松识别执行停止的位置。

答案 1 :(得分:3)

好吧,除非我遗漏了某些内容,否则使用yield的方法根本不起作用。试试吧:

e = Enumerator.new do |y|
  y << 1
  y << 2
  y << 3
end

f = Enumerator.new do
  yield 1
  yield 2
  yield 3
end

e.each { |x| puts x }
f.each { |x| puts x }

产生这个:

telemachus ~ $ ruby yield.rb 
1
2
3
yield.rb:13:in `block in <main>': no block given (yield) (LocalJumpError)
        from yield.rb:19:in `each'
        from yield.rb:19:in `each'
        from yield.rb:19:in `<main>

当他说(第304页)“你这样做”时,他并不是说“这不是最佳方式。”他的意思是,“那不行。”

编辑:但是,您可以通过以下方式明确调用yield:

e = Enumerator.new do |y|
  y.yield 1
  y.yield 2
  y.yield 3
end

如果您发现yield<<更明确或更清晰,那就这样做吧。

第二次编辑:看看大卫的原始帖子和Jorg的最新答案,我认为最初有关于这个问题的混淆。 Jorg认为大卫问的是Enumerator::Yielder#yieldEnumerator::Yielder::<<之间的区别,但大卫不确定 The Well Grounded Rubyist 是什么意思“当不写{{} 1}}等“我的回答适用于关于 The Well Grounded Rubyist 的问题。 (当我今天回顾这个帖子时,根据其他更新,我的答案看起来很奇怪。)

答案 2 :(得分:2)

Enumerator::Yielder#yield方法和Enumerator::Yielder::<<方法完全相同。事实上,它们是别名。

因此,您使用的那两个中的哪一个是100%个人偏好,就像Enumerable#collectEnumerable#mapEnumerable#injectEnumerable#reduce一样。