Project

General

Profile

Misc #15529 ยป document-enumerator-lazy.patch

zverok (Victor Shepelev), 01/12/2019 02:20 PM

View differences:

enumerator.c
1573 1573
    return generator;
1574 1574
}
1575 1575

  
1576
/*
1577
 * Document-class: Enumerator::Lazy
1578
 * Enumerator::Lazy is a special type of Enumerator, that allows constructing
1579
 * chains of operations without evaluating them immediately, and evaluating
1580
 * values on as-needed basis. In order to do so it redefines most of Enumerable
1581
 * methods so that they just construct another lazy enumerator.
1582
 *
1583
 * Enumerator::Lazy can be constructed from any Enumerable with Enumerable#lazy
1584
 * method.
1585
 *
1586
 *
1587
 *    lazy = (1..Float::INFINITY).lazy.select(&:odd?).drop(10).take_while { |i| i < 30 }
1588
 *    # => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:select>:drop(10)>:take_while>
1589
 *
1590
 * The real enumeration is performed when any of non-redefined Enumerable methods
1591
 * is called, like Enumerable#first on Enumerable#to_a (the latter is aliased as
1592
 * #force for more semantical code):
1593
 *
1594
 *    lazy.first(2)
1595
 *    #=> [21, 23]
1596
 *
1597
 *    lazy.force
1598
 *    #=> [21, 23, 25, 27, 29]
1599
 *
1600
 * Note that most of Enumerable methods that could be called with or without block,
1601
 * on Enumerator::Lazy will always require block:
1602
 *
1603
 *    [1, 2, 3].map       #=> #<Enumerator: [1, 2, 3]:map>
1604
 *    [1, 2, 3].lazy.map  # ArgumentError: tried to call lazy map without a block
1605
 *
1606
 * This class allows idiomatic calculations on long or infinite sequence, as well
1607
 * as chaining of calculations without constructing intermediate arrays.
1608
 *
1609
 * Example of working with slowly calculated sequence:
1610
 *
1611
 *    require 'open-uri'
1612
 *
1613
 *    # This will fetch all URLs before selecting
1614
 *    # necessary data
1615
 *    URLS.map { |u| JSON.parse(open(u).read) }.
1616
 *      select { |data| data.key?('stats') }.
1617
 *      first(5)
1618
 *
1619
 *    # This will fetch URLs one-by-one, only till
1620
 *    # there is enough data to satisfy the condition
1621
 *    URLS.lazy.map { |u| JSON.parse(open(u).read) }.
1622
 *      select { |data| data.key?('stats') }.
1623
 *      first(5)
1624
 *
1625
 */
1626

  
1576 1627
/*
1577 1628
 * call-seq:
1578 1629
 *   Lazy.new(obj, size=nil) { |yielder, *values| ... }
......
1580 1631
 * Creates a new Lazy enumerator. When the enumerator is actually enumerated
1581 1632
 * (e.g. by calling #force), +obj+ will be enumerated and each value passed
1582 1633
 * to the given block. The block can yield values back using +yielder+.
1583
 * For example, to create a method +filter_map+ in both lazy and
1584
 * non-lazy fashions:
1585
 *
1586
 *   module Enumerable
1587
 *     def filter_map(&block)
1588
 *       map(&block).compact
1589
 *     end
1590
 *   end
1634
 * For example, to create a "filter+map" enumerator:
1591 1635
 *
1592
 *   class Enumerator::Lazy
1593
 *     def filter_map
1594
 *       Lazy.new(self) do |yielder, *values|
1595
 *         result = yield *values
1596
 *         yielder << result if result
1597
 *       end
1636
 *   def filter_map(sequence)
1637
 *     Lazy.new(sequence) do |yielder, *values|
1638
 *       result = yield *values
1639
 *       yielder << result if result
1598 1640
 *     end
1599 1641
 *   end
1600 1642
 *
1601
 *   (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
1602
 *       # => [4, 16, 36, 64, 100]
1643
 *   filter_map(1..Float::INFINITY) {|i| i*i if i.even?}.first(5)
1644
 *   #=> [4, 16, 36, 64, 100]
1603 1645
 */
1604 1646
static VALUE
1605 1647
lazy_initialize(int argc, VALUE *argv, VALUE self)
......
1704 1746
 * call-seq:
1705 1747
 *   e.lazy -> lazy_enumerator
1706 1748
 *
1707
 * Returns a lazy enumerator, whose methods map/collect,
1708
 * flat_map/collect_concat, select/find_all, reject, grep, grep_v, zip, take,
1709
 * take_while, drop, and drop_while enumerate values only on an
1710
 * as-needed basis.  However, if a block is given to zip, values
1711
 * are enumerated immediately.
1749
 * Returns an Enumerator::Lazy, which redefines most of Enumerable
1750
 * methods to postpone enumeration and enumerate values only on an
1751
 * as-needed basis.
1712 1752
 *
1713 1753
 * === Example
1714 1754
 *
......
1754 1794
 *   lzy.to_enum(method = :each, *args) {|*args| block} -> lazy_enum
1755 1795
 *   lzy.enum_for(method = :each, *args){|*args| block} -> lazy_enum
1756 1796
 *
1757
 * Similar to Kernel#to_enum, except it returns a lazy enumerator.
1797
 * Similar to Object#to_enum, except it returns a lazy enumerator.
1758 1798
 * This makes it easy to define Enumerable methods that will
1759 1799
 * naturally remain lazy if called from a lazy enumerator.
1760 1800
 *
1761
 * For example, continuing from the example in Kernel#to_enum:
1801
 * For example, continuing from the example in Object#to_enum:
1762 1802
 *
1763 1803
 *   # See Kernel#to_enum for the definition of repeat
1764 1804
 *   r = 1..Float::INFINITY
......
1826 1866
    lazy_map_proc, lazy_map_size,
1827 1867
};
1828 1868

  
1869
/*
1870
 *  call-seq:
1871
 *     lazy.collect { |obj| block } -> lazy_enumerator
1872
 *     lazy.map     { |obj| block } -> lazy_enumerator
1873
 *
1874
 *  Like Enumerable#map, but chains operation to be lazy-evaluated.
1875
 *
1876
 *     (1..Float::INFINITY).lazy.map {|i| i**2 }
1877
 *     #=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>
1878
 *     (1..Float::INFINITY).lazy.map {|i| i**2 }.first(3)
1879
 *     #=> [1, 4, 9]
1880
 */
1881

  
1829 1882
static VALUE
1830 1883
lazy_map(VALUE obj)
1831 1884
{
......
1902 1955
 *  A value <i>x</i> returned by <i>block</i> is decomposed if either of
1903 1956
 *  the following conditions is true:
1904 1957
 *
1905
 *    a) <i>x</i> responds to both each and force, which means that
1906
 *       <i>x</i> is a lazy enumerator.
1907
 *    b) <i>x</i> is an array or responds to to_ary.
1958
 *  * a) <i>x</i> responds to both each and force, which means that
1959
 *    <i>x</i> is a lazy enumerator.
1960
 *  * b) <i>x</i> is an array or responds to to_ary.
1908 1961
 *
1909 1962
 *  Otherwise, <i>x</i> is contained as-is in the return value.
1910 1963
 *
......
1935 1988
    lazy_select_proc, 0,
1936 1989
};
1937 1990

  
1991
/*
1992
 *  call-seq:
1993
 *     lazy.find_all { |obj| block } -> lazy_enumerator
1994
 *     lazy.select   { |obj| block } -> lazy_enumerator
1995
 *     lazy.filter   { |obj| block } -> lazy_enumerator
1996
 *
1997
 *  Like Enumerable#select, but chains operation to be lazy-evaluated.
1998
 */
1938 1999
static VALUE
1939 2000
lazy_select(VALUE obj)
1940 2001
{
......
1957 2018
    lazy_reject_proc, 0,
1958 2019
};
1959 2020

  
2021
/*
2022
 *  call-seq:
2023
 *     lazy.reject { |obj| block } -> lazy_enumerator
2024
 *
2025
 *  Like Enumerable#reject, but chains operation to be lazy-evaluated.
2026
 */
2027

  
1960 2028
static VALUE
1961 2029
lazy_reject(VALUE obj)
1962 2030
{
......
1998 2066
    lazy_grep_proc, 0,
1999 2067
};
2000 2068

  
2069
/*
2070
 *  call-seq:
2071
 *     lazy.grep(pattern)                  -> lazy_enumerator
2072
 *     lazy.grep(pattern) { |obj| block }  -> lazy_enumerator
2073
 *
2074
 *  Like Enumerable#grep, but chains operation to be lazy-evaluated.
2075
 */
2076

  
2001 2077
static VALUE
2002 2078
lazy_grep(VALUE obj, VALUE pattern)
2003 2079
{
......
2037 2113
    lazy_grep_v_proc, 0,
2038 2114
};
2039 2115

  
2116
/*
2117
 *  call-seq:
2118
 *     lazy.grep_v(pattern)                  -> lazy_enumerator
2119
 *     lazy.grep_v(pattern) { |obj| block }  -> lazy_enumerator
2120
 *
2121
 *  Like Enumerable#grep_v, but chains operation to be lazy-evaluated.
2122
 */
2123

  
2040 2124
static VALUE
2041 2125
lazy_grep_v(VALUE obj, VALUE pattern)
2042 2126
{
......
2109 2193
    return Qnil;
2110 2194
}
2111 2195

  
2196
/*
2197
 *  call-seq:
2198
 *     lazy.zip(arg, ...)                  -> lazy_enumerator
2199
 *     lazy.zip(arg, ...) { |arr| block }  -> nil
2200
 *
2201
 *  Like Enumerable#zip, but chains operation to be lazy-evaluated.
2202
 *  However, if a block is given to zip, values are enumerated immediately.
2203
 */
2112 2204
static VALUE
2113 2205
lazy_zip(int argc, VALUE *argv, VALUE obj)
2114 2206
{
......
2177 2269
    lazy_take_proc, lazy_take_size,
2178 2270
};
2179 2271

  
2272
/*
2273
 *  call-seq:
2274
 *     lazy.take(n)               -> lazy_enumerator
2275
 *
2276
 *  Like Enumerable#take, but chains operation to be lazy-evaluated.
2277
 */
2278

  
2180 2279
static VALUE
2181 2280
lazy_take(VALUE obj, VALUE n)
2182 2281
{
......
2212 2311
    lazy_take_while_proc, 0,
2213 2312
};
2214 2313

  
2314
/*
2315
 *  call-seq:
2316
 *     lazy.take_while { |obj| block } -> lazy_enumerator
2317
 *
2318
 *  Like Enumerable#take_while, but chains operation to be lazy-evaluated.
2319
 */
2320

  
2215 2321
static VALUE
2216 2322
lazy_take_while(VALUE obj)
2217 2323
{
......
2259 2365
    lazy_drop_proc, lazy_drop_size,
2260 2366
};
2261 2367

  
2368
/*
2369
 *  call-seq:
2370
 *     lazy.drop(n)               -> lazy_enumerator
2371
 *
2372
 *  Like Enumerable#drop, but chains operation to be lazy-evaluated.
2373
 */
2374

  
2262 2375
static VALUE
2263 2376
lazy_drop(VALUE obj, VALUE n)
2264 2377
{
......
2296 2409
    lazy_drop_while_proc, 0,
2297 2410
};
2298 2411

  
2412
/*
2413
 *  call-seq:
2414
 *     lazy.drop_while { |obj| block }  -> lazy_enumerator
2415
 *
2416
 *  Like Enumerable#drop_while, but chains operation to be lazy-evaluated.
2417
 */
2418

  
2299 2419
static VALUE
2300 2420
lazy_drop_while(VALUE obj)
2301 2421
{
......
2343 2463
    lazy_uniq_proc, 0,
2344 2464
};
2345 2465

  
2466
/*
2467
 *  call-seq:
2468
 *     lazy.uniq                -> lazy_enumerator
2469
 *     lazy.uniq { |item| ... } -> lazy_enumerator
2470
 *
2471
 *  Like Enumerable#uniq, but chains operation to be lazy-evaluated.
2472
 */
2473

  
2346 2474
static VALUE
2347 2475
lazy_uniq(VALUE obj)
2348 2476
{
......
2351 2479
    return lazy_add_method(obj, 0, 0, Qnil, Qnil, funcs);
2352 2480
}
2353 2481

  
2482
#if 0 /* for RDoc */
2483

  
2484
/*
2485
 *  call-seq:
2486
 *     lazy.chunk { |elt| ... }                       -> lazy_enumerator
2487
 *
2488
 *  Like Enumerable#chunk, but chains operation to be lazy-evaluated.
2489
 */
2490
static VALUE lazy_chunk(VALUE self)
2491
{
2492
}
2493

  
2494
/*
2495
 *  call-seq:
2496
 *     lazy.chunk_while {|elt_before, elt_after| bool } -> lazy_enumerator
2497
 *
2498
 *  Like Enumerable#chunk_while, but chains operation to be lazy-evaluated.
2499
 */
2500
static VALUE lazy_chunk_while(VALUE self)
2501
{
2502
}
2503

  
2504
/*
2505
 *  call-seq:
2506
 *     lazy.slice_after(pattern)       -> lazy_enumerator
2507
 *     lazy.slice_after { |elt| bool } -> lazy_enumerator
2508
 *
2509
 *  Like Enumerable#slice_after, but chains operation to be lazy-evaluated.
2510
 */
2511
static VALUE lazy_slice_after(VALUE self)
2512
{
2513
}
2514

  
2515
/*
2516
 *  call-seq:
2517
 *     lazy.slice_before(pattern)       -> lazy_enumerator
2518
 *     lazy.slice_before { |elt| bool } -> lazy_enumerator
2519
 *
2520
 *  Like Enumerable#slice_before, but chains operation to be lazy-evaluated.
2521
 */
2522
static VALUE lazy_slice_before(VALUE self)
2523
{
2524
}
2525

  
2526
/*
2527
 *  call-seq:
2528
 *     lazy.slice_when {|elt_before, elt_after| bool } -> lazy_enumerator
2529
 *
2530
 *  Like Enumerable#slice_when, but chains operation to be lazy-evaluated.
2531
 */
2532
static VALUE lazy_slice_when(VALUE self)
2533
{
2534
}
2535
# endif
2536

  
2354 2537
static VALUE
2355 2538
lazy_super(int argc, VALUE *argv, VALUE lazy)
2356 2539
{
2357 2540
    return enumerable_lazy(rb_call_super(argc, argv));
2358 2541
}
2359 2542

  
2543
/*
2544
 *  call-seq:
2545
 *     enum.lazy -> lazy_enumerator
2546
 *
2547
 *  Returns self
2548
 */
2549

  
2360 2550
static VALUE
2361 2551
lazy_lazy(VALUE obj)
2362 2552
{
......
3255 3445

  
3256 3446
#if 0 /* for RDoc */
3257 3447
    rb_define_method(rb_cLazy, "to_a", lazy_to_a, 0);
3448
    rb_define_method(rb_cLazy, "chunk", lazy_chunk, 0);
3449
    rb_define_method(rb_cLazy, "chunk_while", lazy_chunk_while, 0);
3450
    rb_define_method(rb_cLazy, "slice_after", lazy_slice_after, 0);
3451
    rb_define_method(rb_cLazy, "slice_before", lazy_slice_before, 0);
3452
    rb_define_method(rb_cLazy, "slice_when", lazy_slice_when, 0);
3258 3453
#endif
3259 3454
    rb_define_alias(rb_cLazy, "force", "to_a");
3260 3455