Last active
July 21, 2024 16:57
-
-
Save geeksilva97/95266a1382cf68aaf5407138aceff154 to your computer and use it in GitHub Desktop.
Having fun implementing my own Enumerator in plain Ruby. From scratch.
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
fib = Traversor.new do |yielder| | |
a = b = 1 | |
loop do | |
yielder << a | |
a, b = b, a + b | |
end | |
end | |
simple_iterator = Traversor.new do |y| | |
y << 1 | |
y << 2 | |
y << 3 | |
y << 4 | |
y << 5 | |
y << 6 | |
end | |
pp simple_iterator.next | |
pp simple_iterator.next | |
pp simple_iterator.next | |
puts "=== Fibonacci ===" | |
puts fib.next | |
puts fib.next | |
puts fib.next | |
puts fib.take(10).inspect | |
puts "=== Lazy ===" | |
l = simple_iterator.lazy | |
.filter(&:odd?) | |
.take(1) | |
puts l | |
puts l.to_a.inspect | |
lazy_fib = fib.lazy | |
puts lazy_fib.take(5).to_a.inspect |
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' | |
class Traversor | |
def initialize(&block) | |
@block = block | |
end | |
def rewind | |
start_fiber | |
end | |
def lazy | |
Traversor::Lazy.new(&@block) | |
end | |
def each(&each_block) | |
return self unless block_given? | |
yielder = Traversor::Yielder.new(&each_block) | |
@block.call(yielder) | |
end | |
def next | |
start_fiber unless @fiber | |
value = @fiber.resume | |
raise StopIteration unless value && @fiber.alive? | |
value | |
end | |
def map(&map_block) | |
result = [] | |
each do |item| | |
result << map_block.call(item) | |
end | |
end | |
def filter(&filter_block) | |
result = [] | |
each do |item| | |
result << item if filter_block.call(item) | |
end | |
end | |
def take(n) | |
result = [] | |
# fica um pingue-pongue entre o Traversor atraves do each e o Traversor::Yielder | |
each do |item| | |
result << item | |
break if result.size == n | |
end | |
result | |
end | |
private | |
def start_fiber | |
@fiber = Fiber.new do | |
yielder = Yielder.new | |
@block.call(yielder) | |
end | |
end | |
def fiber_yielder | |
@fiber_yielder ||= Fiber.new do | |
@block.call(self) | |
end | |
end | |
end | |
class Traversor::Yielder | |
def initialize(&yielder_block) | |
@yielder_block = yielder_block | |
end | |
def yield(item) | |
return @yielder_block.call(item) if @yielder_block | |
Fiber.yield(item) | |
end | |
alias << yield | |
def next | |
Fiber.yield(10) | |
end | |
end | |
class Traversor::Lazy | |
def initialize(&block) | |
@block = block | |
end | |
def each(&each_block) | |
traversor = Traversor.new(&@block) | |
traversor.each(&each_block) | |
end | |
def map(&map_block) | |
Traversor::Lazy.new do |yielder| | |
each do |item| | |
yielder << map_block.call(item) | |
end | |
end | |
end | |
def filter(&filter_block) | |
Traversor::Lazy.new do |yielder| | |
each do |item| | |
yieldable = filter_block.call(item) | |
puts "filter was called for item #{item} :: yieldable #{yieldable}" | |
yielder << item if yieldable | |
end | |
end | |
end | |
def take(size) | |
raise ArgumentError.new('attempt to take a negative size') if size.negative? | |
Traversor::Lazy.new do |yielder| | |
count = 0 | |
each do |item| | |
puts "take block executed :: count #{count} | size #{size}" | |
yielder << item if (size - count).positive? | |
count += 1 | |
# checks again to be efficient, not breaking here cause the whole pipeline to execute again till this step again. | |
break if count >= size | |
end | |
end | |
end | |
def lazy | |
self | |
end | |
def to_a | |
results = [] | |
each do |item| | |
results << item | |
end | |
results | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment