mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
add mruby-enum-lazy mrbgem for Enumerable::Lazy
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
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
|
||||
@@ -0,0 +1,150 @@
|
||||
# = 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
|
||||
@@ -0,0 +1,47 @@
|
||||
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
|
||||
Reference in New Issue
Block a user