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
		Add a link
		
	
		Reference in a new issue