module Hamster class List def self.[](*items) list = self.new items.reverse_each { |item| list = list.cons(item) } list end def initialize(head = nil, tail = self) @head = head @tail = tail end # Returns true if the list contains no items. def empty? @tail.equal?(self) end # Returns the number of items in the list. def size if empty? 0 else @tail.size + 1 end end # Returns the first item. def head @head end # Returns a copy of self without the first item. def tail @tail end # Returns a copy of self with it as the head. def cons(item) self.class.new(item, self) end # Calls block once for each item in the list, passing the item as the only parameter. # Returns self def each block_given? or return enum_for(__method__) unless empty? yield(@head) @tail.each { |item| yield(item) } end self end # Returns true if . eql? is synonymous with == def eql?(other) return true if other.equal?(self) return false unless other.is_a?(self.class) return true if other.empty? && empty? return other.head == head && other.tail.eql?(tail) end alias :== :eql? # Returns self def dup self end alias :clone :dup def map if empty? self else @tail.map { |item| yield(item) }.cons(yield(@head)) end end def reduce(memo) if empty? memo else @tail.reduce(yield(memo, @head)) { |memo, item| yield(memo, item) } end end private def method_missing(name, *args, &block) if name.to_s =~ /^c([ad]+)r$/ accessor($1) else super end end # Perform compositions of car and cdr operations. Their names consist of a 'c', followed by at # least one 'a' or 'd', and finally an 'r'. The series of 'a's and 'd's in each function's name is chosen to # identify the series of car and cdr operations that is performed by the function. The order in which the 'a's and # 'd's appear is the inverse of the order in which the corresponding operations are performed. def accessor(sequence) sequence.split(//).reverse!.inject(self) do |memo, char| case char when "a" then memo.head when "d" then memo.tail end end end end end