Bug #21142
openLazy enumerator `.each_with_index` ignores `.take(0)` before it
Description
Minimum code to produce problem:
class Numbers; def each; 100.times { yield _1 }; end; include Enumerable; end
Numbers.new.lazy.take(0).each_with_index.map { _1 }.to_a
Output (at ruby 3.2.7, and 3.3.0):
[0, 1, ..., 99]
Expected output (and was as at ruby 3.1.4):
[]
It works when it opposite ordering: Numbers.new.lazy.each_with_index.take(0).map { _1 }.to_a
.
I suspect it may be related to the change here https://github.com/ruby/ruby/pull/11868/files but I'm not familiar with any of that code. It seems like it replaces an allocated index with a counting index, which my hunch is it works for every value except for 0.
Updated by mame (Yusuke Endoh) 1 day ago
- Assignee set to alanwu (Alan Wu)
@alanwu (Alan Wu) What do you think? cc/ @knu (Akinori MUSHA)
Updated by alanwu (Alan Wu) about 22 hours ago
· Edited
- Assignee deleted (
alanwu (Alan Wu))
The pull request you point to was released with 3.4, but the problem started with 3.2, so it's not due to the change in the pull request.
Updated by Hanmac (Hans Mackowiak) about 22 hours ago
· Edited
You might want a different function.
Enumerable#each_with_index
does reset the Enumerator, while Enumerator#with_index
does not.
p 3.times.lazy.take(0).each_with_index.to_a #[[0, 0], [1, 1], [2, 2]]
p 3.times.lazy.take(0).with_index.to_a #[]
Edit: seems it isn't really correct, lazy.take(0)
seems is different from lazy.take(1)
p 3.times.lazy.take(1).each_with_index.to_a #[[0, 0]]
p 3.times.lazy.take(1).with_index.to_a #[[0, 0]]
Maybe this part is the culprit?
lazy_take_precheck(VALUE proc_entry)
{
struct proc_entry *entry = proc_entry_ptr(proc_entry);
return entry->memo != INT2FIX(0);
}