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

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

“有根据的Rubyist”表明,人们不会“从块中屈服”,但并不能准确地解释正在发生的事情。

谢谢

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

所以,你使用的那两个中的哪一个是100%的个人偏好,就像Enumerable#collectEnumerable#mapEnumerable#injectEnumerable#reduce

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

 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. yield用于调用块。

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

这是真的吗?

 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 `
'

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

 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 `
'

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

但我仍然不清楚“yielder”在做什么。 看起来它正在收集所有计算出的值,以便以后在使用枚举器时可以反刍它们。 如果是这种情况,那么它似乎只适用于“小”序列......你不会想要制作一个存储5000万件物品的枚举器。

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

 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 << 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应该被解释为:“在这里停止执行,当next被调用时生成val,然后在下一行继续执行。就我个人而言,我认为语法y << valy.yield valy.yield val ,因此,更容易在代码中找到并轻松识别执行停止的位置。

好吧,除非我遗漏了某些东西,否则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 
': no block given (yield) (LocalJumpError) from yield.rb:19:in `each' from yield.rb:19:in `each' from yield.rb:19:in `

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

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

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

如果你发现说yield<<更明确或更清楚,那就这样做吧。

第二次编辑:看看大卫的原始post和Jorg的最新答案,我认为最初有关于这个问题的混淆。 Jorg认为大卫正在询问Enumerator::Yielder#yieldEnumerator::Yielder::<<之间的区别,但David不确定The Well Grounded Rubyist何时表示“不写yield 1等”。 我的回答适用于关于The Well Grounded Rubyist的问题。 (当我今天回顾这个post时,根据其他更新,我的答案看起来很奇怪。)