Prolog

O-Prologの仕様、覚書

はじめに

自作Prolog処理系のO-Prologについてその仕様などを文書化することにしました。O-PrologはISO-Prolog準拠です。

起動オプション

Windowsであればコマンドプロンプトよりoplと入力することにより起動します。Linuxであれば./oplで起動します。スタートアップファイルを読み込ませて起動する場合には -c でファイル名を与えます。



./ opl -c init.pl

linuxバージョン、ラズパイバージョンのREPLはデフォルトで編集可能になっています。Emacsなどで使うために編集機能が不要の場合には -r オプションで起動してください。

./opl -r

スタートアップファイル

起動時に startup.pl というファイルがあれば、これをまず読み込みます。ここにエディタやREP編集モードのことを書いておけば、お好みの状態で起動します。

例えば下記はREPL編集モードをOFFにする場合です。

:- set_editor(repl,off).

対話的動作

起動すると|(縦棒)記号が表示されます。これがO-Prologのプロンプトです。この状態で受け取ったものは述語定義であると解釈してメモリに記憶されます。|に続いて?-と入力してから入力したものは処理系に対しての質問となります。いずれの場合には最後に .(ピリオド)記号が必要となります。質問の場合にはyesかnoかの結果が返されます。


| human(taro).

| ?- human(taro)).
yes

入力したものを再度入力したい場合には出↑キーで履歴を呼び出すことができます。これは編集可能です。履歴を遡りすぎて戻したいときには下キーです。

Prologコードをあらかじめエディタを使ってファイルにしておき、それを読み込む場合には次のようにします。

| ?- consult('init.pl').
yes
|

既に読み込み済みのコードを上書きをする場合にはreconsultを使います。
| ?- reconsult('init.pl').
yes

reconsultと入力するのは煩わしいのでリストで入力することも可能です。
| ?- ['init.pl'].
yes
|

これらにより読み込んだコードはlisting述語により表示することができます。
| ?-listing.
yes_or_no(X) :- repeat,write(yes or no >),read(X),X==yes;X==no,!.
yes
|

linux版には組み込みのCUIエディタがあります。CPUパワーの小さいラズパイでの編集を目的としています。詳細は下記を参照してください。
http://qiita.com/sym_num/items/4af619de02191c95e3b9

事実

太郎は人間であるという事実は述語として表現されます。

human(taro).

推論規則

人間は誤りを犯すものである、ということは推論規則として表現されます。次のような節となります。

節   ↓通称メダカ記号
error(X) :- human(X).
↑      ↑
頭部     本体部

大文字のアルファベットで始まる記号は変数として扱われます。

アトム

humanやtaroという記号はアトムと呼ばれます。アトムとして利用可能な文字はISO標準のものに加え、UnicodeまたはシフトJISが可能となっています。

アルファベット abcdefghijklmnopqrstuvwz ABCDEFGHIJKLMNOPQRSTUVWXYZ
特殊記号 #$&*+-/:.<=>?@^~\
数文字 1234567890

先頭はアルファベットである必要があります。
特殊記号を含むアトムである場合にはシングルクオートでくくる必要があります。

アトムにシングルクオートを含む場合には次のように記述します。
例 'I don''t know'

| ?- X = 'I don''t know'.
X = I don't know
yes
| 

独自拡張 Unicode(UTF8) またはシフトJIS 例 人間 太郎
Winodws版は初期設定はシフトJISです。Linux版はUnicodeです。
組込み述語 char_set/1により切り替えることができまs。
char_set(unicode). 以後Unicodeでの動作となります。
char_set(sjis).  以後シフトJISでの動作となります。

変数

先頭が大文字のアルファベットで始まるアトムは変数となります。

例 X Y A1 Bcd

先頭がアンダーバーで始まるアトムも変数となります。

例 _var _あれ

整数と浮動小数点数が扱えます。整数は無限多倍長整数(BIGNUM)が扱えます。

| ?- X is sin(3.14).
X = 0.001592652916486828
yes
| ?- Y is 3**100.
Y = 515377520732011331036461129765621272702107522001
yes
|

浮動小数点数には指数表示が可能です。
例 1.002E3

| ?- X is 1.002E3.
X = 1002.0
yes
| 

浮動小数点数は小数部を省略することはできません。
例 1. × 1.0 ○

| ?- X is 1.
X = 1
yes
| ?- X is 1.0.
X = 1.0
yes
| 

整数につき2進、8進、16進表記が使えます。


0o1234 (8)
0b0101 (2)
0xFACE (16) または0xface (16)

O-Prolog Ver0.58
| ?- X is 0o1234.
X = 668
yes
| ?- X is 0b0101.
X = 5
yes
| ?- X is 0xFACE.
X = 64206
yes
| 

リスト

Lispと同様にリストが使えます。ドット記法は|(縦棒)で表現します。以下、Lispとの対比表です。

Prolog       Lisp
[1,2,3]     (1 2 3)
[1,2|3]     (1 2 . 3)
[1,2,[3,4]]  (1 2 (3 4))
[a|b]       (a . b)

演算子記号

次の記号が演算子として使用されています。

":-" "-->" "," "?-" "->" "/\" "=.." "==" "\==" "@<" "@=<" "@>" "@>="
"=:=" "=/=" "=\=" "<" "=<" ">"
">=" "<<" ">>" "**" "\\=" "^"
"=" "+" "-" "*" "//" "/" "."

演算子

:- 節を定義します。左側に頭部、右側に本体部の述語が入ります。
human(X) :- error(X).

?- 質問をします。続けて質問したい述語を記述します。
-? human(taro).

算術式

is述語を使った場合、右辺は評価計算され左辺にユニフィケーションします。この場合の算術式については下記の通りです。

定数

pi 円周率の近似値です。3.14159265358979

関数 数演算子

+ 加算
- 減算
* 乗算
/ 除算
// 整数除算
** 累乗
^ 累乗
abs(X) 絶対値
sin(X) 正弦
cos(X) 余弦
tan(X) 正接
asin(X) 正弦の逆関数
acos(X) 余弦の逆関数
atan(X) 正接の逆関数
exp(X) 指数関数
log(X) 対数関数(底はe)
floor(X) Xに最も近い整数
ceiling(X) 小数部の切り上げ
truncate(X) ゼロから近づいた時の最大の整数
float(X) 整数の浮動小数点数への変換
sign(X) 正負符号 プラスなら1、マイナスなら-1、ゼロなら0
sqrt(X) 平方根
round(X) まるめ
gcd(X,Y) 最大公約数
lcm(X,Y) 最小公倍数
mod(X,Y) 剰余
rem(X,y) 剰余
X /\ Y 論理積
X \/ Y 論理和
X xor Y 排他的論理和
X iand Y 包含的論理積
X << Y 左方向へのビットシフト
X >> Y 右方向へのビットシフト

独自拡張の乱数の組込み関数があります。
random(N) N以下の整数の乱数を生成します。

組込み述語

O-PrologはISO-Prologに準拠しています。さらに入出力などに伝統的なエディンバラPrologの述語を拡張しています。以下、ISO-Prolog以外の組込述語について記述します。

ISO-Prologについては1993年の下記のドラフトに記載された内容のみを尊重します。これ以外の組込述語についてはソフネック社のAZ-Prologのサブセットとし互換性を持たせます。
http://fsl.cs.illinois.edu/images/9/9c/PrologStandard.pdf

assert/1
述語、節をデータベースの最後に追加登録します。

| ?-assert(human(taro)).
yes
| ?-listing.
human(taro).
yes
| 

put/1
引数で与えられた整数を文字コードとみた場合tの文字を表示します。

| ?-put(80).
Pyes
|

get/1
1文字入力し、その文字コードと引数をunifyします。空白文字は無視します。
| ?-get(X).
a
X = 97
yes
|

get0/1
1文字入力し、その文字コードと引数をunifyします。空白文字も1文字として入力します。
| ?-get0(X).
      (スペースを入力しています。)
X = 32
yes
|

not/1
引数の否定を返します。
| ?-not(true).
no
| ?-not(fail).
yes
|

ISO-Prologでfail_if/1がnotに代わります。

listing/0
listing/1
引数無しに実行された場合には、assertされた節をすべて表示します。
引数として非数値アトムが与えられた場合には、そのアトムを述語名とする節を表示します。

consult/1
引数の非数値アトムで指定されたファイルを読み込みます。

reconsult/1
ファイルを読み込みます。consultと違うのは定義を上書きするところです。

see/1
引数のアトムをファイル名とするファイルを入力ようにオープンする。

seen/1
入力ファイルをクローズする。

tell/1
引数のアトムをファイル名とするファイルを出力用にオープンする。

told/1
出力ファイルをクローズする。

trace/0
実行時のデバッグ情報を表示します。

プロンプトを表示しつつステップ実行をします。利用できるコマンドは下記の通りです。

return key: creep
a: abort to REPL
c: creep
d: display goal
e: end of intepreter
?: help
h: help
l: display local stack
n: notrace
s: skip to next spy point
t: display trail stack
?>

notrace/0
traceを解除します。

spy/1
引数に与えられた非数値アトムを述語名とする述語が実行された場合に、その述語につきデバッグ情報を表示します。項数とともに次のように指定します。

 spy(foo/3).

spy/0
spy/1で指定されたスパイポイントを表示します。

nospy/1
spyによるスパイポイントを解除します。

例 
 nospy(foo/3)

nospy/0
spyにより設定されたスパイポイントをすべて解除します。

length/2
第一引数のリストの長さを第二引数にユニファイします。

| ?-length([1,2,3],X).
X = 3
yes
| 

gbc/0 gbc/1
ガーベジコレクションを制御します。引数無しで呼ばれた場合にはガーベジコレクションを起動します。引数にnoを与えた場合にはガーベジコレクション起動時にメッセージを表示しなくなります。引数にyesを与えた場合にはガーベジコレクション起動時にメッセージを表示します。初期設定は表示しないです。

| ?-gbc.
enter GBC free=9999739
exit  GBC free=9999751
yes
| ?-gbc(yes).
yes
| ?-gbc(no).
yes
| ?-gbc.
yes
| 

time/1
実行時間計測用の述語です。計測した述語を引数として与えます。

| ?-time(queens9).
[1,3,6,4,8,2,7,5,9]
...
[9,7,4,6,2,8,3,5,1]
Elapsed Time=0.135000 (second)
no
| 

name/2
アトムとリストとを相互に変換します。

| ?-name(abc,X).
X = [97,98,99]
yes
| ?-name(X,[97,98,99]).
X = abc
yes
| 

debug/1
処理系のデバッグ用です。引数にonを与えると述語や節をS式で表示します。offを与えると元に戻ります。

nano/1
Linuxバージョンでnanoエディタを起動します。引数として編集したいファイル名を渡します。nanoエディタから抜け出た時には編集後のファイルをreconsultします。


?- nano('queens.pl').

timer/1
マイクロ秒単位の精度を持つタイマーです。

timer(on) タイマーをオンにします。
timer(off) タイマーをオフにします。
timer(X) 変数が与えられた場合には、タイマーがオンになっていた時間を秒単位でXにunifyします。精度は1マイクロ秒です。

append/3
第一引数と第二引数とを連結したものを第三引数にunifyします。
 
member/2
第一引数が第二引数のリストのメンバーである場合に真、そうでない場合に偽を返します。

reverse/2
第一引数を反転したものを第二引数にunifyします。

retractall/1
第一引数のアトムをfunctorとする述語、節を削除します。

between/3
下記の述語を組込みにしてあります。
between(L, H, L) :- L =< H.
between(L, H, V) :- L < H, L1 is L + 1, between(L1, H, V).

select/3
下記の述語を組込みにしてあります。
selects([], Ys).
selects([X | Xs], Ys) :- select(X, Ys, Ys1), selects(Xs, Ys1).

sort/2
第一引数のリストをソートして第二引数にunifyします。

edit/1
Linux版、ラズパイ版では組込みエディタを起動します。
詳細は下記を参照のこと。
https://qiita.com/sym_num/items/4af619de02191c95e3b9

ファイル操作関係

make_directory/1
引数で与えられたアトムのディレクトリを作成します。

directory_exists/1
ディレクトリが存在するなら真、そうでないなら偽を返します。

current_directory/1
カレントディレクトリを引数にunifyします。

change_directory/1
ディレクトリを引数のアトムに与えられたものにチェンジします。

expand_path/2
第一引数に与えられたパスを絶対パスに変更して第二引数にunifyします・。

delete_file/1
引数で与えられたファイルを削除します。

file_exists/1
引数で与えられたファイルが存在すれば真を、そうでなければ偽を返します。

decompose_file_name/4
第一引数で与えられたパスを分解して第二引数に以降にunifyします。

| ?- decompose_file_name('\\windows\\foo\\test.pl',X,Y,Z).
X = '\\windows\\foo\\
yes

environment_variable/2
第一引数で与えられた環境変数の値を第二引数にunifyします。

file_modification_time/2
第一引数のファイルが更新された日時秒を第二引数にunifyします。

| ?- file_modification_time('test.pl',X).
X = stamp(2018,7,21,13,4,7)
yes
| 

コンパイラ

コンパイラが用意されています。まだ開発途上ながら末尾再帰最適化が導入されています。
詳細は下記を参照のこと。
https://qiita.com/sym_num/items/e6e0032f2547010dfb3e
https://qiita.com/sym_num/items/8b92cf6f1503474792f2

TCP/IP

Linux版、ラズパイ版にはTCP/IPのための述語があります。
https://qiita.com/sym_num/items/8e5f537bb345f49c1b98