add mruby-enum-lazy mrbgem for Enumerable::Lazy

parent 4e5ec199
......@@ -53,6 +53,9 @@ MRuby::GemBox.new do |conf|
# Use Enumerator class (require mruby-fiber)
conf.gem :core => "mruby-enumerator"
# Use Enumerable::Lazy class (require mruby-enumerator)
conf.gem :core => "mruby-enum-lazy"
# Use extended toplevel object (main) methods
conf.gem :core => "mruby-toplevel-ext"
......
MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Enumerable::Lazy class'
spec.add_dependency('mruby-enumerator')
end
# = Enumerable#lazy implementation
#
# Enumerable#lazy returns an instance of Enumerable::Lazy.
# You can use it just like as normal Enumerable object,
# except these methods act as 'lazy':
#
# - map collect
# - select find_all
# - reject
# - grep
# - drop
# - drop_while
# - take_while
# - flat_map collect_concat
# - zip
#
# == 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)
module Enumerable
def lazy
Lazy.new(self)
end
class Lazy < Enumerator
def initialize(obj, &block)
super(){|yielder|
begin
obj.each{|x|
if block
block.call(yielder, x)
else
yielder << x
end
}
rescue StopIteration
end
}
end
def map(&block)
Lazy.new(self){|yielder, val|
yielder << block.call(val)
}
end
alias collect map
def select(&block)
Lazy.new(self){|yielder, val|
if block.call(val)
yielder << val
end
}
end
alias find_all select
def reject(&block)
Lazy.new(self){|yielder, val|
if not block.call(val)
yielder << val
end
}
end
def grep(pattern)
Lazy.new(self){|yielder, val|
if pattern === val
yielder << val
end
}
end
def drop(n)
dropped = 0
Lazy.new(self){|yielder, val|
if dropped < n
dropped += 1
else
yielder << val
end
}
end
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
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
def take_while(&block)
Lazy.new(self){|yielder, val|
if block.call(val)
yielder << val
else
raise StopIteration
end
}
end
def flat_map(&block)
Lazy.new(self){|yielder, val|
ary = block.call(val)
# TODO: check ary is an Array
ary.each{|x|
yielder << x
}
}
end
alias collect_concat flat_map
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
alias force to_a
end
end
assert("Enumerable::Lazy") do
a = [1, 2]
assert_equal Enumerable::Lazy, a.lazy.class
end
assert("Enumerable::Lazy laziness") do
a = Object.new
def a.each
return to_enum :each unless block_given?
self.b << 10
yield 1
self.b << 20
yield 2
self.b << 30
yield 3
self.b << 40
yield 4
self.b << 50
yield 5
end
def a.b(b=nil)
@b = b if b
@b
end
a.b([])
assert_equal [1,2], a.each.lazy.take(2).force
assert_equal [10,20], a.b
a.b([])
assert_equal [2,4], a.each.lazy.select{|x|x%2==0}.take(2).force
assert_equal [10,20,30,40], a.b
a.b([])
assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(1).force
assert_equal [10], a.b
a.b([])
assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(4).force
assert_equal [10,20], a.b
end
assert("Enumerable::Lazy#zip with cycle") do
e1 = [1, 2, 3].cycle
e2 = [:a, :b].cycle
assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3)
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment