非決定性プログラム
SICP 4.3.2 - ロバの耳ではcallccを使わずに済ませたけれど,そろそろcallccを使えるんじゃないかと思って挑戦してみた.
…
80行もあるすごくブサイクなやつが出来た….しかもかなり使いにくい.
関西オープンソース2005発表, 非決定性計算, KOF宴会 - Journal InTime(2005-10-29)のコードと見比べてみると,1つのchooseにつきcallccを1つしか使わなかったのが敗因らしい.
大まかな流れは理解できていると思うんだけれど,sk.call(choice.call)とfk.call(:fail)の意味が分らない.
ほとんどまるパクリのバージョン
class Amb class ExhaustedError < RuntimeError; end def initialize(c=nil) @fail = c @fail ||= proc { raise ExhaustedError, "amb tree exhausted" } end def choose(*choices) prev_fail = @fail callcc {|sk| choices.each {|x| callcc {|fk| @fail = proc { @fail = prev_fail fk.call() } sk.call(x) } } @fail.call } end def try_again @fail.call end def assert(cond) try_again unless cond end end def amb(&block) ret = [] callcc {|c| a = Amb.new(c) ret << block.call( a) a.try_again } ret end
p amb {|x| a = x.choose(1, 2, 3) b = x.choose(*%w[a b]) [a , b] } #=> [[1, "a"], [1, "b"], [2, "a"], # [2, "b"], [3, "a"], [3, "b"]]
お約束の
amb {|a| baker = a.choose(1, 2, 3, 4, 5) cooper = a.choose(1, 2, 3, 4, 5) fletcher = a.choose(1, 2, 3, 4, 5) miller = a.choose(1, 2, 3, 4, 5) smith = a.choose(1, 2, 3, 4, 5) a.assert([baker, cooper, fletcher, miller, smith].uniq.length == 5) a.assert(baker != 5) a.assert(cooper != 1) a.assert(fletcher != 1 && fletcher != 5) a.assert(miller > cooper) a.assert((smith - fletcher).abs != 1) a.assert((fletcher - cooper).abs != 1) p [baker, cooper, fletcher, miller, smith] } #=> [3, 2, 4, 5, 1]