2012年7月2日月曜日

core.logic

clojureのcontribをながめてるとcore.logicなるものが。

なにやらClojureで論理プログラミングをするためのものらしいのでいろいろと試してみた。

よく例に出るappendの例。

(use 'clojure.core.logic)
(defne append [x y z]
([() a a])
([[a . b] _ [a . c]]
(append b y c)))
view raw append1.clj hosted with ❤ by GitHub

xとyが連結するリスト、zが連結後のリストです。
xが空ならyとzは同じ。
xの先頭とzの先頭が同じなら、xのテールとyを連結したzのテールになる。

普通の関数で書いたらこんな感じ?

(use '[clojure.core.match :only [match]])
(defn append [x y]
(match [x]
[[]] y
[[h & t]] (recur t (cons h y))))
view raw append2.clj hosted with ❤ by GitHub


しかし、普通の関数と違ってかなり高機能です。

実行してみます。

(assert (= (run* [q] (append [1 2] [3 4] q))
'((1 2 3 4))))
(assert (= (run* [q] (append [1 2] q [1 2 3 4]))
'((3 4))))
(assert (= (run* [q] (fresh [a b]
(== q [a b])
(append a b [1 2 3 4])))
'([() (1 2 3 4)] [(1) (2 3 4)] [(1 2) (3 4)] [(1 2 3) (4)] [(1 2 3 4) ()])))
view raw append3.clj hosted with ❤ by GitHub

なんと、append一つ定義するだけで連結・差分・組み合わせの三つの使い方が出来るのです!

おー、論理プログラミングすげー

さらに、ClojureScript版も用意されています。

上記のappendのコードは動きますが、clojure.core.logicと比べると定義されている関数やマクロは少ないです。
バグってたりします。

数独も解いてみた。

(use 'clojure.core.logic)
(def _1 [()])
(def _2 [() ()])
(def _3 [() () ()])
(def _4 [() () () ()])
(defne different [list]
([()])
([[x . ()]])
([[x . [h . t]]]
(fresh [l]
(!= x h)
(conso x t l)
(different l))))
(defne lte [m n]
([() _])
([[_ . x] [_ . y]]
(lte x y)))
(defne gte [m n]
([_ ()])
([[_ . x] [_ . y]]
(gte x y)))
(defne bounds [l m n]
([() _ _])
([[h . t] m n]
(lte m h)
(gte n h)
(bounds t m n)))
(defne sudoku [puzzle]
([[s11 s12 s13 s14
s21 s22 s23 s24
s31 s32 s33 s34
s41 s42 s43 s44]]
(bounds [s11 s12 s13 s14
s21 s22 s23 s24
s31 s32 s33 s34
s41 s42 s43 s44] _1 _4)
(different [s11 s12 s13 s14])
(different [s21 s22 s23 s24])
(different [s31 s32 s33 s34])
(different [s41 s42 s43 s44])
(different [s11 s21 s31 s41])
(different [s12 s22 s32 s42])
(different [s13 s23 s33 s43])
(different [s14 s24 s34 s44])
(different [s11 s12 s21 s22])
(different [s13 s14 s23 s24])
(different [s31 s32 s41 s42])
(different [s33 s34 s43 s44])))
(run* [q] (sudoku [_3 _1 _2 _4 _4 _2 _3 _1 _1 _3 _4 _2 _2 _4 _1 _3]))
view raw sudoku.clj hosted with ❤ by GitHub

数字の扱いが微妙なのでリストで数字を表してみました。

しかしこのコード、正しいパズルかどうかはテストしてくれますが、パズルを解決してくれません。

!=を使っているから・・・・?

まあ、まだまだ開発途中なので今後に期待です。