./index.html ../index.html

Prologに挑戦した時の記録

Haskellのところで、世の中には逐次実行型以外の言語があるという話をしましたが、関数型と双璧を…成しているようにはどーも思えない論理記述型の代表Prologです。 最近は関数型言語の特徴が一部OOPLに導入されてきたりしてますが、今も昔も論理型言語はサッパリ目にしません。 いやま、素人の言うことなんか気にしないでくださいな。

実用レベルに習得するかどうかは別にして、一度は理解ぐらいはしたかった、つーことで、さわりだけやります。

処理系はここ。 教科書はここ

インストールして、まず

むう、拡張子が.plでPerlと被りますね。 でもまあ、関連付けはいじられないようなので(Perlのままになってます)、別にいいか。 コマンドラインからC:\xxx.plみたいな形でPrologのコードを実行するなんてことは、まず無いでしょう。

まずは勘で、[Navigator...]からdemo\like.plを、[Consult]。 % c:/program files/pl/demo/likes.pl compiled 0.00 sec, 2,212 bytesなどと出ましたから、取り込めたのでしょう。 ちなみに[Edit]でメモ帳が開きました。

Prologの文法なんて僕は一切知らないのですが…でも、%%はコメントじゃないかな?ファイルの先頭に%% ?- likes(sam, dahl).みたいに並んでいるのは、これを打ってみろ、ということじゃないかな?ぐらいの推測はできるので、やってみます。

?- likes(sam, dahl).

Yes
?- 

当たりっぽい。 全部入れてみましたが、カレー以外は何でも食うそうです。

ならば、未定義の単語を入れてみましょう。

?- likes(sam,dorayaki).

No
?-

エラーにはならないようですね。

Prologときたら、聞きかじりの知識でもって言いますが、逆算です。 A = B + Cと書いた場合、普通の言語ではBとCからAを求めることしかできませんが、Prologでは逆にAとBからCを求めたりできるっぽい…と、物の本では。

?- likes(sam,X).

X = dahl [ENTER]

Yes
?-

ひとつだけで終わり?

むむう、いや待ってください、キー入力を求めてきたということは…。 よぉし試行錯誤だ(←マニュアル読まない奴)。 ピリオド…だめ。セミコロン…お?二発目にして当たりか?

?- likes(sam,X).

X = dahl ;

X = tandoori ;

X = kurma ;

X = chow_mein ;

X = chop_suey ;

X = sweet_and_sour ;

X = pizza ;

X = spaghetti ;

X = chips ;

No
?-

今日はこの辺にして、次から普通にやっていきますか。
それにしても、Ctrl+Cが、コピーと同時にインタプリタにも送られて、変な動きをするのはどうしたものでしょう。

構文

小文字は記号(固有名詞)、大文字が変数。 ''で囲っても記号。""で囲ったら文字コードのリスト。

個々の定義は"."で終わる。

事実は、likes(doraemon, dorayaki).のように書く。

関係は、brother(A, B) :- father(P, A), father(P, B).のように書く。","はand。 ひとつの述語に複数の定義を与えることもでき、その場合or。

…これだけでは自分で定義した知識に対して自分で問い合わせをするという、虚しい遊び以外に何もできそうに無いので、もう少し勉強してみます。

演算子

is 計算結果を単一化(関数型言語で言うところの束縛と思えばいいのかな)。

=:= いこーる。
=\= のっといこーる。(殺意を覚えるのは気のせいか)
> >= < <= この辺は普通。

= 式のままで単一化?
\= =の逆?意味不明。
== 両辺が完全に同一かどうかの判断、らしい。式とか書いても計算してくれない。
\== ==の逆。

=@= 式の形が同じ形をしているかどうかの比較みたい。
\=@= =@=の逆。
@< @=< @> @>= 式をstandard orderとかいう順に並べて比較する?

+ - * / まあ普通。
// 整数の割り算。ゼロ方向に切り捨てみたいです。
mod 余り。
/\ ビットand。
\/ ビットor。
# ビットxor。
\ ビットnot。(そろそろ殺意が確信に…)
<< >> シフト演算。

integer(X) float(X) 型変換。
abs(X) 絶対値。
min(X, Y) max(X, Y) 最小、最大。
msb(X) 符号ビットの位置らしいです。
round(X), truncate(X), floor(X), ceiling(X) 順にPascalのRound, Trunc, Floor, Ceil。
sqrt, exp, log, sin, cos...とかも普通に存在するらしいです。

階乗

factorial.pl

factorial(0, 1).
factorial(N, F) :- N > 0, N1 is N - 1, factorial(N1, R), F is N * R.

最初N1を作らずにいきなりN - 1を使ったのですが、ダメでした、 引数渡しは=:=ではなく=のようです。

?- factorial(10,X).

X = 3628800 

Yes

うまくいきました。 では逆を…と思ったら。

?- factorial(Y, 6).
ERROR: Arguments are not sufficiently instantiated
^  Exception: (7) _G157>0 ? creep

なんでしょう、これは? 1 is X.だけでもエラーを吐きますから、式が混じるとだめなのでしょうか。 売りのはず(と僕が勝手に思っている)逆算が、この程度で使えなくなるのであれば、価値は無いんじゃないか、とか思ってみたりして。

is の計算に方向性があるからです

ちゃんと書いてありました。 (引用元 http://bach.scitec.kobe-u.ac.jp/prolog/intro/symb.html)。

factorial(1, 1).
factorial(N, F) :- atomic(N), N > 0, N1 is N - 1, factorial(N1, R), F is N * R.
factorial(N, F) :- G is F // 2, factorial_r(N, G, 2).

factorial_r(N, 1, N).
factorial_r(N, F, C) :- D is C + 1, G is F // D, factorial_r(N, G, D).

やったー、逆算版書けましたよ。 atomicの切り分けが味噌ですね。これを外すとエラーになります。 後、マッチする組み合わせを列挙してやろうと、セミコロンを打ったり、factorial(X, Y).のようにしてもエラーです。 Noだとバックトラックしてくれるのですが、エラーだとバックトラックしてくれないっぽいです。 意外に不自由なものですね。

?- factorial(10,X).

X = 3628800 

Yes
?- factorial(X,3628800).

X = 10 

Yes

それと、1000や2000の階乗*1とかやってみたのですが、オーバーフローしました。 SICStus Prologでは(Rubyなどのように)自動で多倍長整数に切り替えてくれる、らしい、ですが、SWI-Prologでは、そんなサービスは無いようです。

正規表現モドキ

結局Prologは、言語としてはパターンマッチを行うのみで、計算なんかは二の次の様です。 式の答えよりも、式の構造の方が大事な感じ。

パターンマッチとくれば…ねえ。

regexp.pl

match([], []).

match([X|A], [X|B]) :- match(A, B).

match([e(X)|A], [X|B]) :- match(A, B).
match([e(X)|A], B) :- match(A, B).

match([m(X)|A], [X|B]) :- match([m(X)|A], B).
match([m(X)|A], B) :- match(A, B).

match([o(X, Y)|A], B) :- match([X|A], B).
match([o(X, Y)|A], B) :- match([Y|A], B).

パワーポイントを見続けると意識を失うので、という理屈で自分を偽って、せっせとノートに書き散らしたコードですが、完璧に動いてくれましたね。

コンパイル済み正規表現のマッチを行います。 ?をe、|をo、*をmで表して、match([e(1), o(2, 3), m(4)], [1,2,4,4]).な感じです。

代入を行っていないため、逆算ができます。 スタックオーバーフローする場合もありますが、途中を補ったりとかできますので、お試しあれ。

結局Prologってのは、バックトラックあり、逆方向の演繹あり、の、NFAシミュレータなんですね。 カットとかもまだやってませんが、それらは実用のための機能。 関数型言語との違いは理解しましたので、この辺が引き時かもしれません。


*1 ☆師匠とJavaScript対Adaで2000の階乗対決をやった記憶が。