Joel VanderWerf said:
>
> Here is an amusing little control structure that shows off ruby's
> continuations and is actually useful (of course, you might want to use
> something other than a global variable--that's just for simplicity):

Stuff like this is so cool ...

A while back I wrote a Ruby version of the (amb) function fopund in
"Learning Scheme in Fixnum Days" (see http://tinyurl.com/pys4).  It, too,
uses continuations to fallback to earlier choices.

Here is an example using the amb function (well, object in the Ruby
version).  See the Scheme link above for a good description of amb.

# BEGIN AMB EXAMPLE -------------------------------------------------
require 'amb'

class MathSolver
  attr_reader :x, :y, :z

  # Initialize x, y and z to be chosen from the integers 1-5.
  def initialize
    @amb = Amb.new
    @x = @amb.choose(1,2,3,4,5)
    @y = @amb.choose(1,2,3,4,5)
    @z = @amb.choose(1,2,3,4,5)
  end

  # Assert that the sum of x, y and z is 9.
  def solve
    @amb.assert(@x+@y+@z == 9)
  end

  # Move on to the next solution.  Throw an exception if
  # there are no more solutions.
  def next
    @amb.failure
  end
end

begin
  s = MathSolver.new
  loop {
    s.solve
    puts "X=#{s.x}, Y=#{s.y}, Z=#{s.z}"
    s.next
  }
rescue Amb::ExhaustedError
  puts "Done"
end
# END EXAMPLE ----------------------------------------------------

The output of the program is ...

$ ruby amb_example.rb
X=1, Y=3, Z=5
X=1, Y=4, Z=4
X=1, Y=5, Z=3
X=2, Y=2, Z=5
X=2, Y=3, Z=4
X=2, Y=4, Z=3
X=2, Y=5, Z=2
X=3, Y=1, Z=5
X=3, Y=2, Z=4
X=3, Y=3, Z=3
X=3, Y=4, Z=2
X=3, Y=5, Z=1
X=4, Y=1, Z=4
X=4, Y=2, Z=3
X=4, Y=3, Z=2
X=4, Y=4, Z=1
X=5, Y=1, Z=3
X=5, Y=2, Z=2
X=5, Y=3, Z=1
Done

I suppose I should post the code for Amb.  Here it is ...

# BEGIN AMB CODE --------------------------------------------------
class Amb
  class ExhaustedError < RuntimeError; end

  def initialize
    @fail = proc { fail ExhaustedError, "amb tree exhausted" }
  end

  def choose(*choices)
    prev_fail = @fail
    callcc { |sk|
      choices.each { |choice|
	callcc { |fk|
	  @fail = proc {
	    @fail = prev_fail
	    fk.call(:fail)
	  }
	  if choice.respond_to? :call
	    sk.call(choice.call)
	  else
	    sk.call(choice)
	  end
	}
      }
      @fail.call
    }
  end

  def failure
    choose
  end

  def assert(cond)
    failure unless cond
  end
end
# END CODE -----------------------------------------------------------

-- 
-- Jim Weirich     jim / weirichhouse.org    http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)