如果有人能帮我理解在枚举器中使用Yielder与在枚举器中调用yield之间的区别,我将不胜感激。
“有条不紊的Rubyist”表明,人们不会“从块中屈服”,但并不能准确解释发生了什么。
由于
答案 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。
需要注意的一些重要事项:
yield在方法
调用方法时,可以将块传递给方法
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 `<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#yield
和Enumerator::Yielder::<<
之间的区别,但大卫不确定 The Well Grounded Rubyist 是什么意思“当不写{{} 1}}等“我的回答适用于关于 The Well Grounded Rubyist 的问题。 (当我今天回顾这个帖子时,根据其他更新,我的答案看起来很奇怪。)
答案 2 :(得分:2)
Enumerator::Yielder#yield
方法和Enumerator::Yielder::<<
方法完全相同。事实上,它们是别名。
因此,您使用的那两个中的哪一个是100%个人偏好,就像Enumerable#collect
和Enumerable#map
或Enumerable#inject
和Enumerable#reduce
一样。