diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2014-04-04 10:33:17 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2014-04-04 10:33:17 +0900 |
| commit | 7a9630a5bb9534ab266aa225d728935ad648b861 (patch) | |
| tree | 9c25558f3b4369461e4fd1a6a5c78452c918d2f5 | |
| parent | 4e5ec1993adb7f156edb08b9077734f8f9ef8b48 (diff) | |
| download | mruby-7a9630a5bb9534ab266aa225d728935ad648b861.tar.gz mruby-7a9630a5bb9534ab266aa225d728935ad648b861.zip | |
add mruby-enum-lazy mrbgem for Enumerable::Lazy
| -rw-r--r-- | mrbgems/default.gembox | 3 | ||||
| -rw-r--r-- | mrbgems/mruby-enum-lazy/mrbgem.rake | 6 | ||||
| -rw-r--r-- | mrbgems/mruby-enum-lazy/mrblib/lazy.rb | 150 | ||||
| -rw-r--r-- | mrbgems/mruby-enum-lazy/test/lazy.rb | 47 |
4 files changed, 206 insertions, 0 deletions
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox index 2f436e5b6..1bba7be26 100644 --- a/mrbgems/default.gembox +++ b/mrbgems/default.gembox @@ -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" diff --git a/mrbgems/mruby-enum-lazy/mrbgem.rake b/mrbgems/mruby-enum-lazy/mrbgem.rake new file mode 100644 index 000000000..45dda4054 --- /dev/null +++ b/mrbgems/mruby-enum-lazy/mrbgem.rake @@ -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 diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb new file mode 100644 index 000000000..088ce760d --- /dev/null +++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb @@ -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 diff --git a/mrbgems/mruby-enum-lazy/test/lazy.rb b/mrbgems/mruby-enum-lazy/test/lazy.rb new file mode 100644 index 000000000..ca009d34c --- /dev/null +++ b/mrbgems/mruby-enum-lazy/test/lazy.rb @@ -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 |
