Class: Enumerator::Lazy

Inherits:
Enumerator show all
Defined in:
enumerator.c

Instance Method Summary collapse

Methods inherited from Enumerator

#each, #each_with_index, #each_with_object, #feed, #initialize_copy, #inspect, #next, #next_values, #peek, #peek_values, #rewind, #size, #with_index, #with_object

Methods included from Enumerable

#all?, #any?, #count, #cycle, #detect, #each_cons, #each_entry, #each_slice, #each_with_index, #each_with_object, #entries, #find, #find_index, #first, #group_by, #include?, #inject, #max, #max_by, #member?, #min, #min_by, #minmax, #minmax_by, #none?, #one?, #partition, #reduce, #reverse_each, #sort, #sort_by, #to_a

Constructor Details

#new(obj, size = nil) {|yielder, *values| ... } ⇒ Object

Creates a new Lazy enumerator. When the enumerator is actually enumerated (e.g. by calling #force), obj will be enumerated and each value passed to the given block. The block can yield values back using yielder. For example, to create a method filter_map in both lazy and non-lazy fashions:

module Enumerable
  def filter_map(&block)
    map(&block).compact
  end
end

class Enumerator::Lazy
  def filter_map
    Lazy.new(self) do |yielder, *values|
      result = yield *values
      yielder << result if result
    end
  end
end

(1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
    # => [4, 16, 36, 64, 100]

Yields:

  • (yielder, *values)

1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
# File 'enumerator.c', line 1305

static VALUE
lazy_initialize(int argc, VALUE *argv, VALUE self)
{
    VALUE obj, size = Qnil;
    VALUE generator;

    rb_check_arity(argc, 1, 2);
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy new without a block");
    }
    obj = argv[0];
    if (argc > 1) {
	size = argv[1];
    }
    generator = generator_allocate(rb_cGenerator);
    rb_block_call(generator, id_initialize, 0, 0, lazy_init_block_i, obj);
    enumerator_init(self, generator, sym_each, 0, 0, 0, size);
    rb_ivar_set(self, id_receiver, obj);

    return self;
}

Instance Method Details

#chunkObject


1844
1845
1846
1847
1848
# File 'enumerator.c', line 1844

static VALUE
lazy_super(int argc, VALUE *argv, VALUE lazy)
{
    return enumerable_lazy(rb_call_super(argc, argv));
}

#collectObject


1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
# File 'enumerator.c', line 1439

static VALUE
lazy_map(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy map without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_map_func, 0),
			   Qnil, lazy_receiver_size);
}

#flat_map {|obj| ... } ⇒ Object

Returns a new lazy enumerator with the concatenated results of running block once for every element in lazy.

["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
#=> ["f", "o", "o", "b", "a", "r"]

A value x returned by block is decomposed if either of the following conditions is true:

a) <i>x</i> responds to both each and force, which means that
   <i>x</i> is a lazy enumerator.
b) <i>x</i> is an array or responds to to_ary.

Otherwise, x is contained as-is in the return value.

[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]

Yields:

  • (obj)

1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
# File 'enumerator.c', line 1523

static VALUE
lazy_flat_map(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_flat_map_func, 0),
			   Qnil, 0);
}

#dropObject


1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
# File 'enumerator.c', line 1807

static VALUE
lazy_drop(VALUE obj, VALUE n)
{
    long len = NUM2LONG(n);

    if (len < 0) {
	rb_raise(rb_eArgError, "attempt to drop negative size");
    }
    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_drop_func, n),
			   rb_ary_new3(1, n), lazy_drop_size);
}

#drop_whileObject


1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
# File 'enumerator.c', line 1833

static VALUE
lazy_drop_while(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy drop_while without a block");
    }
    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_drop_while_func, 0),
			   Qnil, 0);
}

#to_enum(method = :each, *args) ⇒ Object #enum_for(method = :each, *args) ⇒ Object #to_enum(method = :each, *args) {|*args| ... } ⇒ Object #enum_for(method = :each, *args) {|*args| ... } ⇒ Object

Similar to Kernel#to_enum, except it returns a lazy enumerator. This makes it easy to define Enumerable methods that will naturally remain lazy if called from a lazy enumerator.

For example, continuing from the example in Kernel#to_enum:

# See Kernel#to_enum for the definition of repeat
r = 1..Float::INFINITY
r.repeat(2).first(5) # => [1, 1, 2, 2, 3]
r.repeat(2).class # => Enumerator
r.repeat(2).map{|n| n ** 2}.first(5) # => endless loop!
# works naturally on lazy enumerator:
r.lazy.repeat(2).class # => Enumerator::Lazy
r.lazy.repeat(2).map{|n| n ** 2}.first(5) # => [1, 1, 4, 4, 9]

Overloads:

  • #to_enum(method = :each, *args) {|*args| ... } ⇒ Object

    Yields:

    • (*args)
  • #enum_for(method = :each, *args) {|*args| ... } ⇒ Object

    Yields:

    • (*args)

1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
# File 'enumerator.c', line 1414

static VALUE
lazy_to_enum(int argc, VALUE *argv, VALUE self)
{
    VALUE lazy, meth = sym_each;

    if (argc > 0) {
	--argc;
	meth = *argv++;
    }
    lazy = lazy_to_enum_i(self, meth, argc, argv, 0);
    if (rb_block_given_p()) {
	enumerator_ptr(lazy)->size = rb_block_proc();
    }
    return lazy;
}

#find_allObject


1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
# File 'enumerator.c', line 1546

static VALUE
lazy_select(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy select without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_select_func, 0),
			   Qnil, 0);
}

#flat_map {|obj| ... } ⇒ Object

Returns a new lazy enumerator with the concatenated results of running block once for every element in lazy.

["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
#=> ["f", "o", "o", "b", "a", "r"]

A value x returned by block is decomposed if either of the following conditions is true:

a) <i>x</i> responds to both each and force, which means that
   <i>x</i> is a lazy enumerator.
b) <i>x</i> is an array or responds to to_ary.

Otherwise, x is contained as-is in the return value.

[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]

Yields:

  • (obj)

1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
# File 'enumerator.c', line 1523

static VALUE
lazy_flat_map(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_flat_map_func, 0),
			   Qnil, 0);
}

#grepObject


1605
1606
1607
1608
1609
1610
1611
1612
1613
# File 'enumerator.c', line 1605

static VALUE
lazy_grep(VALUE obj, VALUE pattern)
{
    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 rb_block_given_p() ?
					 lazy_grep_iter : lazy_grep_func,
					 pattern),
			   rb_ary_new3(1, pattern), 0);
}

#lazyObject


1850
1851
1852
1853
1854
# File 'enumerator.c', line 1850

static VALUE
lazy_lazy(VALUE obj)
{
    return obj;
}

#mapObject


1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
# File 'enumerator.c', line 1439

static VALUE
lazy_map(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy map without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_map_func, 0),
			   Qnil, lazy_receiver_size);
}

#rejectObject


1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
# File 'enumerator.c', line 1569

static VALUE
lazy_reject(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy reject without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_reject_func, 0),
			   Qnil, 0);
}

#selectObject


1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
# File 'enumerator.c', line 1546

static VALUE
lazy_select(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy select without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_select_func, 0),
			   Qnil, 0);
}

#slice_beforeObject


1844
1845
1846
1847
1848
# File 'enumerator.c', line 1844

static VALUE
lazy_super(int argc, VALUE *argv, VALUE lazy)
{
    return enumerable_lazy(rb_call_super(argc, argv));
}

#takeObject


1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
# File 'enumerator.c', line 1736

static VALUE
lazy_take(VALUE obj, VALUE n)
{
    long len = NUM2LONG(n);
    VALUE lazy;

    if (len < 0) {
	rb_raise(rb_eArgError, "attempt to take negative size");
    }
    if (len == 0) {
	VALUE len = INT2NUM(0);
	lazy = lazy_to_enum_i(obj, sym_cycle, 1, &len, 0);
    }
    else {
	lazy = rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_take_func, n);
    }
    return lazy_set_method(lazy, rb_ary_new3(1, n), lazy_take_size);
}

#take_whileObject


1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
# File 'enumerator.c', line 1765

static VALUE
lazy_take_while(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy take_while without a block");
    }
    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_take_while_func, 0),
			   Qnil, 0);
}

#to_enum(method = :each, *args) ⇒ Object #enum_for(method = :each, *args) ⇒ Object #to_enum(method = :each, *args) {|*args| ... } ⇒ Object #enum_for(method = :each, *args) {|*args| ... } ⇒ Object

Similar to Kernel#to_enum, except it returns a lazy enumerator. This makes it easy to define Enumerable methods that will naturally remain lazy if called from a lazy enumerator.

For example, continuing from the example in Kernel#to_enum:

# See Kernel#to_enum for the definition of repeat
r = 1..Float::INFINITY
r.repeat(2).first(5) # => [1, 1, 2, 2, 3]
r.repeat(2).class # => Enumerator
r.repeat(2).map{|n| n ** 2}.first(5) # => endless loop!
# works naturally on lazy enumerator:
r.lazy.repeat(2).class # => Enumerator::Lazy
r.lazy.repeat(2).map{|n| n ** 2}.first(5) # => [1, 1, 4, 4, 9]

Overloads:

  • #to_enum(method = :each, *args) {|*args| ... } ⇒ Object

    Yields:

    • (*args)
  • #enum_for(method = :each, *args) {|*args| ... } ⇒ Object

    Yields:

    • (*args)

1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
# File 'enumerator.c', line 1414

static VALUE
lazy_to_enum(int argc, VALUE *argv, VALUE self)
{
    VALUE lazy, meth = sym_each;

    if (argc > 0) {
	--argc;
	meth = *argv++;
    }
    lazy = lazy_to_enum_i(self, meth, argc, argv, 0);
    if (rb_block_given_p()) {
	enumerator_ptr(lazy)->size = rb_block_proc();
    }
    return lazy;
}

#zipObject


1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
# File 'enumerator.c', line 1674

static VALUE
lazy_zip(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary, v;
    long i;
    rb_block_call_func *func = lazy_zip_arrays_func;

    if (rb_block_given_p()) {
	return rb_call_super(argc, argv);
    }

    ary = rb_ary_new2(argc);
    for (i = 0; i < argc; i++) {
	v = rb_check_array_type(argv[i]);
	if (NIL_P(v)) {
	    for (; i < argc; i++) {
		if (!rb_respond_to(argv[i], id_each)) {
		    rb_raise(rb_eTypeError, "wrong argument type %s (must respond to :each)",
			rb_obj_classname(argv[i]));
		}
	    }
	    ary = rb_ary_new4(argc, argv);
	    func = lazy_zip_func;
	    break;
	}
	rb_ary_push(ary, v);
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 func, ary),
			   ary, lazy_receiver_size);
}