gpt4 book ai didi

ruby - 为什么在 `.each` 之后调用惰性枚举时 `.select` 返回 nil?

转载 作者:数据小太阳 更新时间:2023-10-29 06:35:55 25 4
gpt4 key购买 nike

我有一段代码是这样的:

sent_messages = messages.lazy.reject { |m| message_is_spam?(m) }
.each { |m| send_message(m) }
# Do something with sent_messages...

某些上下文:如果消息的收件人在过去 5 分钟内收到消息,则 message_is_spam? 方法返回 true。当 messages 包含发给同一收件人的多封邮件时,只有在发送第一封邮件后,后一封邮件才会被视为垃圾邮件。为了确保后一条消息被视为垃圾邮件,我懒惰地拒绝垃圾邮件并发送它们。

我希望 .each 返回一个包含所有项目的数组,但我得到的却是 nil.each 总是返回一个数组,除了这种情况:

[].each {}                # => []
[].lazy.each {} # => []
[].select {}.each {} # => []
[].lazy.select {}.each {} # => nil

更令人困惑的是,JRuby 在上面的所有示例中都返回了 []

为什么 .each 在这样调用时返回 nil?我在文档中找不到任何关于它的信息,而且很难弄清楚 C 代码中发生了什么。

我已经找到了完全绕过这个问题的方法;如果我为每个收件人选择最多 1 条消息 (messages.uniq_by(&:recipient)),操作不再需要是惰性的。尽管如此,这仍然让我感到惊讶。

最佳答案

可能的解释

Enumerator::Lazy的目的之一|是为了避免内存中有一个巨大的(或可能是无限的)数组。这可以解释为什么 Enumerator#each不返回所需的数组。

与其冒用巨大数组耗尽内存的风险,不如使用 Lazy#reject 这样的方法。更喜欢返回 nil 作为替代值(之后由 each 返回的值):

return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_reject_funcs);

相比之下,Enumerable#lazy 返回:

VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size);

我怀疑不同的论点:

  • Qnil 代表拒绝
  • sym_each 用于惰性

原因是:

  • [].lazy.each {} 返回 []
  • [].lazy.select{}.each {} 返回 nil

不过,each返回数组还是nil似乎并不一致.

备选方案

每个

您的代码的更详细的替代方案可能是:

messages = %w(a b c)
messages_to_send = messages.lazy.reject{|x| puts "Is '#{x}' spam?"}
messages_to_send.each{ |m| puts "Send '#{m}'" }
# Is 'a' spam?
# Send 'a'
# Is 'b' spam?
# Send 'b'
# Is 'c' spam?
# Send 'c'

Lazy#reject 返回一个 Lazy 枚举器,因此第二个 message_is_spam? 将在第一个 send_message 之后执行>.

但是有一个问题,在惰性枚举器上调用 to_a 将再次调用 reject :

sent_messages = messages_to_send.to_a
# Is 'a' spam?
# Is 'b' spam?
# Is 'c' spam?

map和修改方法

您也可以在 send_message 的末尾返回 m 并使用 Lazy#map :

sent_messages = messages.lazy.reject { |m| message_is_spam?(m) }
.map { |m| send_message(m) }.to_a

map 应该可靠地返回所需的 Enumerator::Lazy 对象。来电 Enumerable#to_a确保 sent_messages 是一个数组。

map 和显式返回

如果您不想修改send_message,您可以在每次map 迭代结束时显式返回m:

messages = %w(a b c)

sent_messages = messages.lazy.reject{ |m| puts "Is '#{m}' spam?" }
.map{ |m| puts "Send '#{m}'"; m }.to_a
# Is 'a' spam?
# Send 'a'
# Is 'b' spam?
# Send 'b'
# Is 'c' spam?
# Send 'c'

p sent_messages
# ["a", "b", "c"]

修改后的逻辑

另一种选择是在没有 lazy 的情况下重新定义您的逻辑:

sent_messages = messages.map do |m|
next if message_is_spam?(m)
send_message(m)
m
end.compact

关于ruby - 为什么在 `.each` 之后调用惰性枚举时 `.select` 返回 nil?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43499849/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com