如果有人能帮助我理解在枚举器中使用 Yielder 与在枚举器中调用 yield 之间的区别,我将不胜感激。
“扎实的 Rubyist”建议人们不会“从块中屈服”,但没有准确解释正在发生的事情。
谢谢
如果有人能帮助我理解在枚举器中使用 Yielder 与在枚举器中调用 yield 之间的区别,我将不胜感激。
“扎实的 Rubyist”建议人们不会“从块中屈服”,但没有准确解释正在发生的事情。
谢谢
如果您首先了解 yield 的工作原理,它可能会有所帮助。这是一个例子:
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 异常。所以在这两种情况下,枚举器都无法产生值。
但我仍然不清楚“屈服者”在做什么。看起来它正在收集所有计算值,以便以后使用枚举器时可以反刍它们。如果是这种情况,那么它似乎只适用于“小”序列......你不会想要一个存储 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
因为消息“just executed y << val”没有出现在输出中,这意味着执行必须在 line 上停止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 << val
比 更突出y.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 <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
的比 更明确或更清晰<<
,那么就这样做。
第二次编辑:查看 David 的原始帖子和 Jorg 的更新答案,我认为这个问题最初存在混淆。Enumerator::Yielder#yield
Jorg 认为 David 是在询问和之间的区别Enumerator::Yielder::<<
,但 David 不确定The Well Grounded Rubyist所说的“不要写yield 1
等”是什么意思。我的回答适用于有关The Well Grounded Rubyist的问题。(当我今天回顾这个帖子时,鉴于其他更新,我的回答看起来很奇怪。)
Enumerator::Yielder#yield
方法和方法Enumerator::Yielder::<<
完全一样。事实上,它们是别名。
因此,您使用这两者中的哪一个是 100% 个人偏好,就像Enumerable#collect
andEnumerable#map
或Enumerable#inject
and一样Enumerable#reduce
。