Class: Enumerator::Lazy

Inherits:
Enumerator
  • Object
show all
Defined in:
mrbgems/mruby-enum-lazy/mrblib/lazy.rb

Overview

Acknowledgements

Based on https://github.com/yhara/enumerable-lazy
Inspired by https://github.com/antimon2/enumerable_lz
http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja)

Instance Method Summary collapse

Constructor Details

#initialize(obj, &block) ⇒ Lazy

call-seq:

Lazy.new(obj, &block)

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 by calling yielder.yield. For example, to create a method that acts like Array#select:

def select
Lazy.new(self) do |yielder, value|
  yielder.yield(value) if yield(value)
end
end


59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 59

def initialize(obj, &block)
  super(){|yielder|
    begin
      obj.each {|x|
        if block
          block.call(yielder, x)
        else
          yielder << x
        end
      }
    rescue StopIteration
    end
  }
end

Instance Method Details

#drop(n) ⇒ Object

call-seq:

lazy.drop(n) -> lazy_enumerator

Like Enumerable#drop, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.drop(3).first(3)
#=> [4, 5, 6]


206
207
208
209
210
211
212
213
214
215
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 206

def drop(n)
  dropped = 0
  Lazy.new(self){|yielder, val|
    if dropped < n
      dropped += 1
    else
      yielder << val
    end
  }
end

#drop_while(&block) ⇒ Object

call-seq:

lazy.drop_while {|obj| block } -> lazy_enumerator

Like Enumerable#drop_while, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.drop_while {|i| i < 4 }.first(3)
#=> [4, 5, 6]


226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 226

def drop_while(&block)
  dropping = true
  Lazy.new(self){|yielder, val|
    if dropping
      if not block.call(val)
        yielder << val
        dropping = false
      end
    else
      yielder << val
    end
  }
end

#flat_map(&block) ⇒ Object Also known as: collect_concat

call-seq:

lazy.flat_map {|obj| block } -> lazy_enumerator
lazy.collect_concat {|obj| block } -> lazy_enumerator

Like Enumerable#flat_map, but chains operation to be lazy-evaluated.

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


292
293
294
295
296
297
298
299
300
301
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 292

def flat_map(&block)
  Lazy.new(self){|yielder, val|
    result = block.call(val)
    if result.respond_to?(:each)
      result.each {|x| yielder << x }
    else
      yielder << result
    end
  }
end

#grep(pattern) ⇒ Object

call-seq:

lazy.grep(pattern) -> lazy_enumerator

Like Enumerable#grep, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.grep(1..10).force
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


172
173
174
175
176
177
178
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 172

def grep(pattern)
  Lazy.new(self){|yielder, val|
    if pattern === val
      yielder << val
    end
  }
end

#grep_v(pattern) ⇒ Object

call-seq:

lazy.grep_v(pattern) -> lazy_enumerator

Like Enumerable#grep_v, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.grep_v(2..4).first(3)
#=> [1, 5, 6]


189
190
191
192
193
194
195
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 189

def grep_v(pattern)
  Lazy.new(self){|yielder, val|
    unless pattern === val
      yielder << val
    end
  }
end

#map(&block) ⇒ Object Also known as: collect

call-seq:

lazy.map {|obj| block } -> lazy_enumerator
lazy.collect {|obj| block } -> lazy_enumerator

Like Enumerable#map, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.map {|i| i**2 }
#=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>
(1..Float::INFINITY).lazy.map {|i| i**2 }.first(3)
#=> [1, 4, 9]


120
121
122
123
124
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 120

def map(&block)
  Lazy.new(self){|yielder, val|
    yielder << block.call(val)
  }
end

#reject(&block) ⇒ Object

call-seq:

lazy.reject {|obj| block } -> lazy_enumerator

Like Enumerable#reject, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.reject {|i| i.even? }.first(3)
#=> [1, 3, 5]


155
156
157
158
159
160
161
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 155

def reject(&block)
  Lazy.new(self){|yielder, val|
    unless block.call(val)
      yielder << val
    end
  }
end

#select(&block) ⇒ Object Also known as: find_all

call-seq:

lazy.select {|obj| block } -> lazy_enumerator
lazy.find_all {|obj| block } -> lazy_enumerator

Like Enumerable#select, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.select {|i| i.even? }.first(3)
#=> [2, 4, 6]


137
138
139
140
141
142
143
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 137

def select(&block)
  Lazy.new(self){|yielder, val|
    if block.call(val)
      yielder << val
    end
  }
end

#take(n) ⇒ Object

call-seq:

lazy.take(n) -> lazy_enumerator

Like Enumerable#take, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.take(3).force
#=> [1, 2, 3]


249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 249

def take(n)
  if n == 0
    return Lazy.new(self){raise StopIteration}
  end
  taken = 0
  Lazy.new(self){|yielder, val|
    yielder << val
    taken += 1
    if taken >= n
      raise StopIteration
    end
  }
end

#take_while(&block) ⇒ Object

call-seq:

lazy.take_while {|obj| block } -> lazy_enumerator

Like Enumerable#take_while, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.take_while {|i| i < 4 }.force
#=> [1, 2, 3]


272
273
274
275
276
277
278
279
280
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 272

def take_while(&block)
  Lazy.new(self){|yielder, val|
    if block.call(val)
      yielder << val
    else
      raise StopIteration
    end
  }
end

#to_enum(meth = :each, *args, &block) ⇒ Object Also known as: enum_for

call-seq:

lazy.to_enum(method = :each, *args)                 -> lazy_enum
lazy.to_enum(method = :each, *args) {|*args| ... }  -> lazy_enum
lazy.enum_for(method = :each, *args)                -> lazy_enum
lazy.enum_for(method = :each, *args) {|*args| ... } -> lazy_enum

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

For example:

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


93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 93

def to_enum(meth=:each, *args, &block)
  unless self.respond_to?(meth)
    raise ArgumentError, "undefined method #{meth}"
  end
  lz = Lazy.new(self, &block)
  obj = self
  lz.instance_eval {
    @obj = obj
    @meth = meth
    @args = args
  }
  lz
end

#uniq(&block) ⇒ Object

call-seq:

lazy.uniq -> lazy_enumerator
lazy.uniq {|item| block } -> lazy_enumerator

Like Enumerable#uniq, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.map {|i| i % 3}.uniq.first(3)
#=> [1, 2, 0]


337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 337

def uniq(&block)
  hash = {}
  Lazy.new(self){|yielder, val|
    if block
      v = block.call(val)
    else
      v = val
    end
    unless hash.include?(v)
      yielder << val
      hash[v] = val
    end
  }
end

#zip(*args, &block) ⇒ Object

call-seq:

lazy.zip(arg, ...) -> lazy_enumerator
lazy.zip(arg, ...) {|arr| block } -> lazy_enumerator

Like Enumerable#zip, but chains operation to be lazy-evaluated. However, if a block is given to zip, values are enumerated immediately.

(1..Float::INFINITY).lazy.zip(('a'..'z').cycle).first(3)
#=> [[1, "a"], [2, "b"], [3, "c"]]


315
316
317
318
319
320
321
322
323
324
325
# File 'mrbgems/mruby-enum-lazy/mrblib/lazy.rb', line 315

def zip(*args, &block)
  enums = [self] + args
  Lazy.new(self){|yielder, val|
    ary = enums.map {|e| e.next}
    if block
      yielder << block.call(ary)
    else
      yielder << ary
    end
  }
end