Skip to content

Instantly share code, notes, and snippets.

@yhara
Created June 16, 2011 03:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yhara/1028609 to your computer and use it in GitHub Desktop.
Save yhara/1028609 to your computer and use it in GitHub Desktop.
Enumerable#lazy proof-of-concept
# = Enumerable#lazy example 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
#
# == Example
#
# This code prints the first 100 primes.
#
# require 'prime'
# INFINITY = 1.0 / 0
# p (1..INFINITY).lazy.map{|n| n**2+1}.
# select{|m| m.prime?}.take(100)
#
# == Acknowledgements
#
# 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)
# def first(n=1)
#
# These methods are intentionally omitted, so that
# we can print the result like
#
# p obj.lazy.map{...}.take(10)
# # => prints the values, since take(10) returns an Array
# # instead of an instance of Enumerable::Lazy
#
# This means that map or select after take(n)
# (eg. obj.lazy.take(10).map{...})
# is not be the lazy-version.
# Since take(n) returns limited number of elements,
# this will not be a problem.
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
# def chunk
# def slice_before
#
# There methods are already implemented with Enumerator.
end
end
# Example
# -- Print the first 100 primes
#require 'prime'
#p (1..1.0/0).lazy.select{|m|m.prime?}.first(100)
# -- Print the first 10 word from a text file
#File.open("english.txt"){|f|
# p f.lines.lazy.flat_map{|line| line.split}.take(10)
#}
# -- Example of cycle and zip
#e1 = [1, 2, 3].cycle
#e2 = [:a, :b].cycle
#p e1.lazy.zip(e2).take(10)
# -- Example of chunk and take_while
#p Enumerator.new{|y|
# loop do
# y << rand(100)
# end
#}.chunk{|n| n.even?}.
# lazy.map{|even, ns| ns}.
# take_while{|ns| ns.length <= 5}.to_a
@mame
Copy link

mame commented Jun 16, 2011

This code prints the first 100 primes.

Doubt! :-)

@yhara
Copy link
Author

yhara commented Jun 16, 2011

Ouch X-(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment