I tried to use the Iterator module for this, but I’m missing something.
I assumed you had to make these iterators, but what’s the correct way to do this in Crystal?
squares = Enumerator.new{ |y| (0..).each{ |n| y << 2**n}}
squares5 = Enumerator.new{ |y| (0..).each{ |n| y << 2**n * 5}}
pyth_quad = Enumerator.new do |y|
n = squares.next
m = squares5.next
loop do
if n < m
y << n
n = squares.next
else
y << m
m = squares5.next
end
end
end
# this takes less than a millisecond
puts pyth_quad.take_while{ |n| n <= 1000000000}.join(" ")
This is what I came up with:
squares = (0..).each.map { |n| 2_i64**n }
squares5 = (0..).each.map { |n| 2_i64**n * 5 }
n = squares.next.as(Int)
m = squares5.next.as(Int)
pyth_quad = Iterator.of do
if n < m
value = n
n = squares.next.as(Int)
else
value = m
m = squares5.next.as(Int)
end
value
end
puts pyth_quad.take_while { |n| n <= 1000000000 }.join(" ")
Thank @asterite, as always.
The only thing I changed was the 2_i64 to 2_u64.
I got about 65-70% right, trying to following the examples in the docs.
I didn’t have the .map on the ends of (0..).each or the .as(Int) on the end of next, or moved m/n = squares/5... outside pyth_quad, or the .of on the end of Iterator.
I’m going to study it more, with the docs, to really understand what/why is going on.
FYI, here are time comparisons with different Ruby versions.
For Ruby ran as: $ time ruby pythquads.rb
For Crystal, compiled as: $ crystal build pythquads.cr --release
Ran as: $ time ./pythquads
Ruby 3.0.2 0.063 secs
Ruby 2.7.4 0.057 secs
Truffleruby 21.2.0.1 0.037 secs
Crystal 1.1.1 0.007 secs
Crystal 1.2.1 0.002 secs
Ideally, the library or the compiler should be smarter about iterators that never finish, but that’s not the case right now. That’s why you need those as(Int) , because the return value of next could be Iterator::Stop.
I tried. To improve that but couldn’t. I’ll open an issue and see if someone can figure this out, or if it’s even worth it.