lazy_stream.rb

# see [Shades of Gray: Infinite Streams](http://blog.grayproductions.net/articles/infinite_streams)

require 'enumerator'

module LazyStream
  class Node
    include Enumerable

    def initialize(head, &promise)
      @head, @tail = head, promise
    end

    def head
      @transformer.nil? ? @head : @transformer[@head]
    end
    alias_method :current, :head

    def tail
      result = if @tail.is_a? Proc
                 @tail.call
               else
                 @tail
               end
      result.filter(@filter) unless @filter.nil? or not result.is_a? self.class
      result.transform(&@transformer) unless @transformer.nil? or not result.is_a? self.class

      result
    end
    alias_method :next, :tail

    def drop
      result, next_stream = head, tail
      @head, @tail = if next_stream.is_a? self.class
                       next_stream.instance_eval { [@head, @tail] }
                     else
                       Array(next_stream)
                     end
      result
    end
    alias_method :tail!, :drop
    alias_method :next!, :drop

    def end?
      @tail.nil?
    end

    def next?
      not end?
    end

    def show(*limit_and_options)
      options = {:sep => " ", :end => "\n"}.merge!(
        limit_and_options.last.is_a?(Hash) ? limit_and_options.pop : Hash.new
        )
      limit = limit_and_options.shift

      each(limit) { |cur| print cur, options[:sep] }
      print options[:end]
    end
    alias_method :display, :show

    def each!(limit = nil)
      loop do
        break unless limit.nil? or (limit -= 1) > -1
        yield drop
        break if end?
      end

      self
    end

    def each(limit = nil, &block)
      clone.each!(limit, &block)
      self
    end
    alias_method :peek, :each

    def limit(max_depth = nil)
      enum_for(:each, max_depth)
    end

    def limit!(max_depth = nil)
      enum_for(:each!, max_depth)
    end

    def filter(pattern = nil, &block)
      @filter = pattern || block

      drop until matches_filter? @head

      self
    end

    def transform(&transformer)
      @transformer = transformer

      self
    end

    private
    def matches_filter?(current)
      case @filter
      when nil
        true
      when Proc
        @filter[current]
      else
        @filter === current
      end
    end
  end
end

module Kernel
  def lazy_stream(*args, &block)
    LazyStream::Node.new(*args, &block)
  end
end