Range Over Iterators in Crystal

Our first program will explore iterating over data and returning an iterator. This lets us range over pretty much anything in the data structure! Here’s the full source code.

class List(T)
  struct Element
    property next : Element?
    property val : T
  end

  property head : Element?
  property tail : Element?

  def push(v : T)
    new_element = Element.new(val: v)
    if @tail.nil?
      @head = new_element
      @tail = new_element
    else
      @tail.not_nil!.next = new_element
      @tail = new_element
    end
  end

  def all
    Iterator(T).new do |yield|
      element = @head
      while element
        break unless yield.call(element.val)
        element = element.next
      end
    end.to_a
  end
end

class Iterator(T)
  def initialize(&block : Proc(T, Bool)::)->Nil)
    @block = block
  end

  def each(&block : (T ->))
    @block.call do |value|
      block.call(value)
      true
    end
  end

  def to_a : Array(T)
    array = [] of T
    each do |element|
      array << element
    end
    array
  end
end

def gen_fib
  Iterator(Int32).new do |yield|
    a = 1
    b = 1
    loop do
      break unless yield.call(a)
      a, b = b, a + b
    end
  end
end

list = List(Int32).new
list.push(10)
list.push(13)
list.push(23)

list_all = list.all
puts "List Elements:"
list_all.each do |e|
  puts e
end

fib_iterator = gen_fib
fib_iterator.each do |n|
  break if n >= 10
  puts n
end

puts "Collected Fib:"
puts fib_iterator.to_a.inspect

In this example, we define a List class in Crystal that can store elements. The all method in the List class returns an iterator. This iterator can be used in a loop to range over all the elements of the list.

We also create a gen_fib function, which returns an iterator over Fibonacci numbers. This keeps running as long as the yield keeps returning true.

To add elements to the list, we create an instance of List and use the push method. We then range over the list using the returned iterator from the all method.

Lastly, we generate and print Fibonacci numbers using the gen_fib function.

This example illustrates the power and flexibility of iterators in Crystal, making it easy to range over custom data structures and implement complex iteration logic seamlessly.