From: Joel VanderWerf Date: 2001-09-01T07:50:01+09:00 Subject: [ruby-talk:20646] Iterating by links This is part of an Enumerable extension package I'm going to submit to RAA eventually, but it's useful, so maybe the ruby-list will forgive the size... Suppose you want to iterate over a linked list, or more generally a sequence of objects that can be generated by iteratively calling some function. You can do this by calling the function in a while loop. Or you can use "by", as in these examples (source follows): class Foo attr_reader :value, :next_foo def initialize value, next_foo = nil @value = value @next_foo = next_foo end end list = Foo.new(0, Foo.new(1, Foo.new(2, Foo.new(3)))) for foo in list.by :next_foo p foo.value end puts for foo in list.by { |foo| foo.next_foo.next_foo } p foo.value end puts tree = [[0, [1, 2]], 3, [4, 5, [6]]] for node in tree.by(:at, 2) p node end puts p 0.by { |x| x<10 ? x+1 : nil }.to_a puts f = proc { |x, incr, limit| y = x + incr; y <= limit ? y : nil } p 0.by(f, 10, 50).to_a puts class A; end class B < A; end class C < B; end for cl in C.by :superclass p cl end puts # Popping down a stack: p [0,1,2,3,4].by { |y| y.pop; y != [] && y.dup }.entries ######################## module Enumerable class LinkedListDelegator include Enumerable attr_reader :first, :next_name, :next_proc, :args def initialize first, next_name_or_proc = nil, *args, &next_proc @first = first @args = args case next_name_or_proc when Symbol @next_name = next_name_or_proc when String @next_name = next_name_or_proc.intern when Proc @next_proc = next_name_or_proc when nil @next_proc = next_proc end end def each cur = @first if @next_name loop do break unless cur yield cur cur = cur.send @next_name, *@args end elsif @next_proc loop do break unless cur yield cur cur = @next_proc[cur, *@args] end else raise ArgumentError, "No method or procedure specified for getting next in LinkedList." end self end end end class Object def by next_name = nil, *args, &next_getter Enumerable::LinkedListDelegator.new self, next_name, *args, &next_getter end end