Humanity

Edit the world by your favorite way

Prolog に関するメモ

REPL でも述語を定義する方法

stackoverflow.com

['user'] で REPL でも述語が定義できる。

| ?- ['user'].
woman(mia).
^D

部分適用

Prolog で部分適用。

?- ['user'].
add(X, Y, X+Y).
|: true.

?- add(1, 2, R).
R = 1+2.

?- call(add, 1, 2, R).
R = 1+2.

?- call(add(1), 1, R).
R = 1+1.

この記事で使われてて知った。

もっと非Prolog的Prologプログラミング

演算子

Prolog演算子を定義したり term_expansion を駆使すれば任意の言語をほぼそのまま評価できそうな気がする。 と思ったけどカンマ等はどうにもならないっぽい。

誰でも動かせるPrologで操作的意味論を書こうぜ

Prolog の op/3 の定義の仕方と、以下の記事の演算子の優先順位の定義の仕方が似てる。 Prolog 処理系もこんな感じでパースしてるんだろうか。 二項演算子以外は Prolog で定義できるのか知らないけど(三項演算子とか前置・後置演算子)。

<Insert Language Name Here> - How to make interesting little languages.

と思って調べたらできるっぽい(前置・後置の単項演算子を定義)。 三項演算子二項演算子の組み合わせだから cond ? expr1 : expr2 では ?:演算子を定義すればいいはず?

Prolog 写経記 その 88 if/1 - 日記

演算子定義のエラー回避

演算子を定義しようとしたけどできない、って時は op(400,xfx,'||'). みたいにシングルクォートで囲ってやれば演算子定義の際のエラーを回避できるかもしれない (よくわかってない)。

Google グループ

または :- multifile (=>)/2. みたいにすれば演算子をエクスポートする際のエラーを回避できるかもしれない (よくわかってない)。

stackoverflow.com

Prolog 初学者向け解説

以下のページがよくまとまってて読みやすかった。 英語だけど分量が少ないし説明が簡潔。

A Concise Introduction To Prolog

あとこの言葉が好き。

Prolog is a notation for stating logical relations that happens to be executable.

というわけで以下上記のページで気になった項目をつまみ食い。

ジェネレーター

述語は複数の値を返せる。

Many predicates can be used as generators, to generate one solution after another until later conditions are met. For example,

?- member(X, [1, 2, 3, 4, 5]), X > 3.
X = 4 ;
X = 5.

succeeds and instantiates X to 4. If backed into, it re-instantiates X to 5. (But if you think declaratively, this just says "X is a member of the list [1, 2, 3, 4, 5] that is greater than 3.'')

例えば以下の記事では前のバージョンのコードでは find_tree という述語を作っていた。

% Path = [], Node = a++[b++[a++[]]] になる (複数個の回答は考えられてなかった…はず)
find_tree(a++[b++[a++[]]], =(a++_), Path, Node).

でも Prolog ではそのような述語を作る必要が無く、単にツリーを traverse して各ノードを返すジェネレーターを作ればそれで事足りる。 例えば以下の様に(テストコードから引用)。

% a++_ にマッチするノードをリストアップ。ここでは以下のペアが返される。
% Path = [], Node = a++[b++[a++[]]]
% Path = [0, 0], Node = a++[]
traverse_tree(a++[b++[a++[]]], Path, Node), Node = a++_.

ジェネレーターのそれぞれの返り値をリストにしたければ findall/3 を使う。 1番目の引数には2番目の引数の式でキャプチャ (自分語録) したい変数を式の形で示すとその通りに3番目のリストに入る。 ここでは (Path, Node) と指定しているので、 Results は [([], a++[b++[a++[]]]), ([0, 0], a++[])] となる。

Node = a++_, findall((Path, Node), traverse_tree(a++[b++[a++[]]], Path, Node), Results).

Prolog は式をその場で評価しない。 例えば Two = 1+1Two2 ではなく 1+1 になる。 ちなみに数値として評価したい場合は is を使う (Two is 1+1)。

そして (Path, Node) はタプルみたいなもので、C 等で (1+2)*3 とする場合の優先順位を示す括弧とは全く違う (という認識)。 2つの変数の式の形を示せればいいので、2要素のリストとする事もできる。

Node = a++_, findall([Path, Node], traverse_tree(a++[b++[a++[]]], Path, Node), Results).

この例も分かりやすかった。

Simple Prolog generator - Stack Overflow

Cut-Fail

あ、これ再帰の深い所から抜け出して値を返したい時便利そう。 再帰の深い所で失敗した場合、再帰でちまちまバケツリレーしてたけど、cut-fail を使えばシンプルに書ける (はず)。

When one clause fails, the next one is tried. If you want the failure of a clause to cause the failure of the entire predicate, you can use a cut-fail combination:

sqrt(X, RootX) :- X < 0, !, fail.
(more clauses of sqrt should follow)

This is a procedural shortcut that avoids the necessity of having X <= 0 in every clause; it is justified only if the test is complex and there are many clauses.

動的に述語を定義

動的に述語が定義できるらしい。 ただそれが良い書き方かどうかは分からない。 イミュータブルじゃなくなる気がするので、個人的には避けた方が良いような気もする?

Database manipulation predicates

assert(X)Add X to the database. For syntactic reasons, if X is not a base

ツイ