Last active
August 29, 2015 14:06
-
-
Save stujo/7450a9f9e411cbdbc3e2 to your computer and use it in GitHub Desktop.
Enumerator Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'fiber' | |
#https://practicingruby.com/articles/building-enumerable-and-enumerator | |
module FakeEnumerable | |
def select | |
result = [] | |
each do |value| | |
result << value if yield(value) | |
end | |
result | |
end | |
def reduce sym_or_memo = nil | |
memo = 0 | |
if sym_or_memo.is_a? Symbol | |
method_name = sym_or_memo | |
each do |value| | |
memo = memo.send(method_name, value) | |
end | |
elsif block_given? | |
memo = sym_or_memo unless sym_or_memo.nil? | |
each do |value| | |
memo = yield memo, value | |
end | |
end | |
memo | |
end | |
def map(&block) | |
if block_given? | |
result = [] | |
each do |value| | |
result << yield(value) | |
end | |
result | |
else | |
FakeEnumerator.new(self, :map) | |
end | |
end | |
def sort_by | |
result = [] | |
each do |value| | |
result << value | |
end | |
result.sort!{|a,b| | |
yield(a) <=> yield(b) | |
} | |
end | |
end | |
class FakeEnumerator | |
def initialize subject, method_name_symbol = nil | |
@subject = subject | |
@method_name_symbol = method_name_symbol | |
rewind | |
end | |
def with_index(&block) | |
results = [] | |
index = 0 | |
begin | |
while (v = self.next) do | |
results << yield(v, index) | |
index += 1 | |
end | |
rescue StopIteration | |
end | |
results | |
end | |
def next | |
@fiber.resume.tap{raise StopIteration unless @fiber.alive?} | |
end | |
def rewind | |
@fiber = Fiber.new do | |
@subject.each do |element| | |
Fiber.yield element | |
end | |
end | |
end | |
end | |
class SortedList | |
include FakeEnumerable | |
def initialize | |
@data = [] | |
end | |
def <<(new_element) | |
@data << new_element | |
@data.sort! | |
self | |
end | |
def each | |
if block_given? | |
@data.each { |e| yield(e) } | |
else | |
FakeEnumerator.new(self, :each) | |
end | |
end | |
end | |
require "minitest/autorun" | |
describe "FakeEnumerable" do | |
before do | |
@list = SortedList.new | |
# will get stored interally as 3,4,7,13,42 | |
@list << 3 << 13 << 42 << 4 << 7 | |
end | |
it "supports map" do | |
@list.map { |x| x + 1 }.must_equal([4,5,8,14,43]) | |
end | |
it "supports sort_by" do | |
# ascii sort order | |
@list.sort_by { |x| x.to_s }.must_equal([13, 3, 4, 42, 7]) | |
end | |
it "supports select" do | |
@list.select { |x| x.even? }.must_equal([4,42]) | |
end | |
it "supports reduce" do | |
@list.reduce(:+).must_equal(69) | |
@list.reduce { |s,e| s + e }.must_equal(69) | |
@list.reduce(-10) { |s,e| s + e }.must_equal(59) | |
end | |
end | |
describe "FakeEnumerator" do | |
before do | |
@list = SortedList.new | |
@list << 3 << 13 << 42 << 4 << 7 | |
end | |
it "supports next" do | |
enum = @list.each | |
enum.next.must_equal(3) | |
enum.next.must_equal(4) | |
enum.next.must_equal(7) | |
enum.next.must_equal(13) | |
enum.next.must_equal(42) | |
assert_raises(StopIteration) { (enum.next).tap{|x| puts x} } | |
end | |
it "supports rewind" do | |
enum = @list.each | |
4.times { enum.next } | |
enum.rewind | |
2.times { enum.next } | |
enum.next.must_equal(7) | |
end | |
it "supports with_index" do | |
enum = @list.map | |
expected = ["0. 3", "1. 4", "2. 7", "3. 13", "4. 42"] | |
enum.with_index { |e,i| "#{i}. #{e}" }.must_equal(expected) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment