mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Fix Enumerator::Lazy#with_index
* Make it correctly handle lambdas * Make it iterate over the block if block is given The original implementation was flawed, based on lazy_set_method instead of lazy_add_method. Note that there is no implicit map when passing a block, the return value of the block passed to with_index is ignored, just as it is for Enumerator#with_index. Also like Enumerator#with_index, when called with a block, the return value is an enumerator without the index. Fixes [Bug #16414]
This commit is contained in:
parent
8a80bfcfd4
commit
85e43e1dfe
Notes:
git
2019-12-11 12:00:23 +09:00
2 changed files with 71 additions and 67 deletions
121
enumerator.c
121
enumerator.c
|
@ -2645,6 +2645,62 @@ lazy_uniq(VALUE obj)
|
|||
return lazy_add_method(obj, 0, 0, Qnil, Qnil, funcs);
|
||||
}
|
||||
|
||||
static struct MEMO *
|
||||
lazy_with_index_proc(VALUE proc_entry, struct MEMO* result, VALUE memos, long memo_index)
|
||||
{
|
||||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||||
VALUE memo = rb_ary_entry(memos, memo_index);
|
||||
VALUE argv[2];
|
||||
|
||||
if (NIL_P(memo)) {
|
||||
memo = entry->memo;
|
||||
}
|
||||
|
||||
argv[0] = result->memo_value;
|
||||
argv[1] = memo;
|
||||
if (entry->proc) {
|
||||
rb_proc_call_with_block(entry->proc, 2, argv, Qnil);
|
||||
LAZY_MEMO_RESET_PACKED(result);
|
||||
} else {
|
||||
LAZY_MEMO_SET_VALUE(result, rb_ary_new_from_values(2, argv));
|
||||
LAZY_MEMO_SET_PACKED(result);
|
||||
}
|
||||
rb_ary_store(memos, memo_index, LONG2NUM(NUM2LONG(memo) + 1));
|
||||
return result;
|
||||
}
|
||||
|
||||
static const lazyenum_funcs lazy_with_index_funcs = {
|
||||
lazy_with_index_proc, 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* lazy.with_index(offset = 0) {|(*args), idx| ... }
|
||||
* lazy.with_index(offset = 0)
|
||||
*
|
||||
* If a block is given, iterates the given block for each element
|
||||
* with an index, which starts from +offset+, and returns a
|
||||
* lazy enumerator that yields the same values (without the index).
|
||||
*
|
||||
* If a block is not given, returns a new lazy enumerator that
|
||||
* includes the index, starting from +offset+.
|
||||
*
|
||||
* +offset+:: the starting index to use
|
||||
*
|
||||
* see Enumerator#with_index.
|
||||
*/
|
||||
static VALUE
|
||||
lazy_with_index(int argc, VALUE *argv, VALUE obj)
|
||||
{
|
||||
VALUE memo;
|
||||
|
||||
rb_scan_args(argc, argv, "01", &memo);
|
||||
if (NIL_P(memo))
|
||||
memo = LONG2NUM(0);
|
||||
|
||||
return lazy_add_method(obj, 0, 0, memo, rb_ary_new_from_values(1, &memo), &lazy_with_index_funcs);
|
||||
}
|
||||
|
||||
#if 0 /* for RDoc */
|
||||
|
||||
/*
|
||||
|
@ -3841,71 +3897,6 @@ arith_seq_size(VALUE self)
|
|||
return len;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_with_index_func(RB_BLOCK_CALL_FUNC_ARGLIST(val, offset))
|
||||
{
|
||||
VALUE yielder, memo, result;
|
||||
VALUE e = rb_enum_values_pack(argc - 1, argv + 1);
|
||||
long idx;
|
||||
|
||||
yielder = argv[0];
|
||||
memo = rb_attr_get(yielder, id_memo);
|
||||
if (NIL_P(memo))
|
||||
memo = offset;
|
||||
idx = NUM2LONG(memo);
|
||||
result = rb_assoc_new(e, memo);
|
||||
rb_funcall(yielder, idLTLT, 1, result);
|
||||
rb_ivar_set(yielder, id_memo, LONG2NUM(++idx));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_with_index_iter(RB_BLOCK_CALL_FUNC_ARGLIST(val, offset))
|
||||
{
|
||||
VALUE yielder, memo, result;
|
||||
VALUE e = rb_enum_values_pack(argc - 1, argv + 1);
|
||||
long idx;
|
||||
|
||||
yielder = argv[0];
|
||||
memo = rb_attr_get(yielder, id_memo);
|
||||
if (NIL_P(memo))
|
||||
memo = offset;
|
||||
idx = NUM2LONG(memo);
|
||||
result = rb_yield(rb_assoc_new(e, memo));
|
||||
rb_funcall(yielder, idLTLT, 1, result);
|
||||
rb_ivar_set(yielder, id_memo, LONG2NUM(++idx));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* lazy.with_index(offset = 0) {|(*args), idx| ... }
|
||||
* lazy.with_index(offset = 0)
|
||||
*
|
||||
* Iterates the given block for each element with an index, which
|
||||
* starts from +offset+. If no block is given, returns a new
|
||||
* lazy enumerator that includes the index, starting from +offset+
|
||||
*
|
||||
* +offset+:: the starting index to use
|
||||
*
|
||||
* see Enumerator#with_index.
|
||||
*/
|
||||
static VALUE
|
||||
lazy_with_index(int argc, VALUE *argv, VALUE obj)
|
||||
{
|
||||
VALUE memo;
|
||||
|
||||
rb_scan_args(argc, argv, "01", &memo);
|
||||
if (NIL_P(memo))
|
||||
memo = LONG2NUM(0);
|
||||
|
||||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||||
rb_block_given_p() ?
|
||||
lazy_with_index_iter : lazy_with_index_func,
|
||||
memo),
|
||||
rb_ary_new_from_values(argc, argv), 0);
|
||||
}
|
||||
|
||||
void
|
||||
InitVM_Enumerator(void)
|
||||
{
|
||||
|
|
|
@ -647,7 +647,7 @@ EOS
|
|||
def test_with_index
|
||||
feature7877 = '[ruby-dev:47025] [Feature #7877]'
|
||||
leibniz = ->(n) {
|
||||
(0..Float::INFINITY).lazy.with_index {|i, j|
|
||||
(0..Float::INFINITY).lazy.with_index.map {|i, j|
|
||||
raise IndexError, "limit exceeded (#{n})" unless j < n
|
||||
((-1) ** j) / (2*i+1).to_f
|
||||
}.take(n).reduce(:+)
|
||||
|
@ -656,7 +656,20 @@ EOS
|
|||
assert_in_epsilon(Math::PI/4, leibniz[1000])
|
||||
}
|
||||
|
||||
ary = (0..Float::INFINITY).lazy.with_index(2) {|i, j| [i-1, j] }.take(2).to_a
|
||||
a = []
|
||||
ary = (0..Float::INFINITY).lazy.with_index(2) {|i, j| a << [i-1, j] }.take(2).to_a
|
||||
assert_equal([[-1, 2], [0, 3]], a)
|
||||
assert_equal([0, 1], ary)
|
||||
|
||||
a = []
|
||||
ary = (0..Float::INFINITY).lazy.with_index(2, &->(i,j) { a << [i-1, j] }).take(2).to_a
|
||||
assert_equal([[-1, 2], [0, 3]], a)
|
||||
assert_equal([0, 1], ary)
|
||||
|
||||
ary = (0..Float::INFINITY).lazy.with_index(2).map {|i, j| [i-1, j] }.take(2).to_a
|
||||
assert_equal([[-1, 2], [0, 3]], ary)
|
||||
|
||||
ary = (0..Float::INFINITY).lazy.with_index(2).map(&->(i, j) { [i-1, j] }).take(2).to_a
|
||||
assert_equal([[-1, 2], [0, 3]], ary)
|
||||
|
||||
ary = (0..Float::INFINITY).lazy.with_index(2).take(2).to_a
|
||||
|
|
Loading…
Add table
Reference in a new issue