Project

General

Profile

Feature #708 ยป lazy_enum.rb

candlerb (Brian Candler), 11/03/2008 06:54 PM

 
1
# Inside Enumerator, redefine those Enumerable methods which return an Array
2
# so that they return another Enumerator instead. This allows Enumerator
3
# chaining, where the calls go 'horizontally' without creating any
4
# intermediate arrays, including processing of infinite enumerations. e.g.
5
#
6
#   class Fib
7
#     def initialize(a=1,b=1)
8
#       @a, @b = a, b
9
#     end
10
#     def each
11
#       a, b = @a, @b
12
#       yield a
13
#       while true
14
#         yield b
15
#         a, b = b, a+b
16
#       end
17
#     end
18
#   end
19
#   
20
#   Fib.new.to_enum.select { |i| i % 2 == 0 }.map { |i| "<#{i}>" }.
21
#     each_with_index { |i,cnt| puts i; break if cnt >= 20 }
22
#
23
# Note: not all methods have been implemented here, just some sample ones.
24

    
25
class Enumerator
26
  def map(&blk) 
27
    self.class.new do |y|
28
      each do |e|
29
        y << blk[e]
30
      end
31
    end
32
  end
33

    
34
  def select(&blk)
35
    self.class.new do |y|
36
      each do |e|
37
        y << e if blk[e]
38
      end
39
    end
40
  end
41

    
42
  def take(n)
43
    self.class.new do |y|
44
      count = 0
45
      each do |e|
46
        break if n <= count
47
        y << e
48
        count += 1
49
      end
50
    end
51
  end
52
  
53
  def skip(n)
54
    self.class.new do |y|
55
      count = 0
56
      each do |e|
57
        y << e unless count <= n
58
        count += 1
59
      end
60
    end
61
  end
62
end
63

    
64
# This reported separately as Feature #666.
65
# This could go in Enumerator, but it makes sense in Enumerable too IMO.
66
module Enumerable
67
  def to_hash
68
    each_with_object({}) { |(k,v),o| o[k] = v }
69
  end
70
end
71

    
72
if __FILE__ == $0
73
  big = (1..1_000_000_000_000).to_enum
74
  big.select { |i| i % 2 == 1 }.map { |i| i + 100 }.skip(5).take(10).
75
      each { |i| puts i }
76

    
77
  # Normal version:
78
  h = {1=>2, 3=>4}
79
  p h.select { true }
80
  # This is the chainable Enumerator version:
81
  p h.to_enum.select { true }.to_hash
82
end