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(" ")
3 Likes
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
3 Likes
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.
1 Like