概要
先日、ISLisp上に作ったPrologインタプリタをライブラリとして整理するとともに、LispからPrologの呼び出し、PrologからLispの呼び出しができるようにしました。ISLispにはILOSというオブジェクト指向の仕組みがありますので、関数型、論理型、オブジェクト型の融合のお遊びができるようになりました。
アイディアの元
後藤滋樹先生の「記号処理プログラミング」 岩波書店の記述がヒントになっています。
PrologからLispの呼び出し
is述語の右辺はLispのevalを呼び出しています。ですから計算式のみならずISLisp関数の全部を呼び出すことができます。これを利用しますとProlog側からlispを呼び出すことができます。
;; call lisp function
(assert ((ack _x _y _z)(is _z (ack _x _y))))
(defun ack (m n)
(cond ((= m 0) (+ n 1))
((= n 0) (ack (- m 1) 1))
(t (ack (- m 1) (ack m (- n 1)))) ))
Easy-ISLisp Ver2.64
> (import "prolog")
T
> (prolog)
-? (consult "tests/test.pl")
YES
-? (ack 3 8 _x)
_X = 2045
YES
-?
このコード中のdefunは組込み述語として定義してあります。Lispと見かけは同じです。Lisp関数としてevalをしています。他にもdefgenericとdefmethodも組み込み述語となっています。
上記の例ではアッカーマン関数を呼び出しています。エジンバラPrologではユーザー定義関数はありません。このためアッカーマン関数も述語で定義することとなっています。しかし、多くの処理系ではスタックオーバーフローを起こします。バックトラックに備えて多くのデータを保持しているためです。決定性の関数である場合にそれは非効率です。
LispからPrologの呼び出し
assert -? ~? を関数として用意してあります。-?はproveを呼び出します。~?はderefを呼び出します。これによりlispでPrologコードを記述し証明を読みだしてlispで利用することが可能です。
;;embedded prolog test code
(assert '(likes kim robin))
(assert '(likes sandy lee))
(assert '(likes sandy kim))
(assert '(likes robin cats))
(assert '((likes sandy _x) (likes _x cats)))
(assert '((likes kim _x) (likes _x lee) (likes _x kim)))
(assert '(likes _x _x))
(defun foo ()
(let ((env (-? '(findall _x (likes sandy _x) _y))))
(~? '_y env)))
Easy-ISLisp Ver2.64
> (import "prolog")
T
> (load "tests/prolog-test.lsp")
T
> (foo)
((LEE) (KIM) (ROBIN) (SANDY) (CATS) (SANDY))
>
Prologの弱点
Prologはバックトラックをするためのデータを保持しておく必要があります。このためアッカーマン関数のような決定性で多くの計算を必要とするものの計算には不向きです。多くのProlog処理系でスタック不足により処理系が落ちます。バックトラックを要しない決定性の関数はLisp書いておけば処理系の負担が軽減されます。Prologの弱点をLispがカバーしつつそれぞれの得意分野を活かすことが可能です。
Prologの可能性
現在主流のAIはDeepLearningです。DLはAIの可能性を切り拓きました。しかし、DL単独では人間のような推論をすることはできません。筋道立てて論理的に考えていくということを模倣するにはPrologのような論理型の言語が必要なはずです。DLとPrologが組み合わさることで可能性が広がるだろうと思います。Lisp&Prologには古典的人工知能の遺産があります。これを現代に活かせないだろうか、と考え続けています。
実装
自作のLisp処理系、Easy-ISLispにライブラリとして付属しています。よかったら遊んでみてください。
https://github.com/sasagawa888/eisl