「Prologで作る数学の世界」をN-Prologで！

# さあ、次はPrologだ
この数年、ずっとLisp世界にいました。自作のEasy-ISLispの並列機能拡張やEmacs風エディタなど環境整備に集中していました。この度それらについてほぼやり尽くしたと思えました。そして中途にしていたprologに再度、取り組んでいます。N-PrologというARITY/PROLOGに互換のインタプリタ、コンパイラです。私にとって当時RUN/PROLOGという名前で安価に販売されていたインタプリタにとても思い入れがあります。さらに数学者、飯高茂先生の著作「Prologで作る数学の世界」もお気に入りです。この本のコードはRUN\PROLOGで書かれています。N-Prologのテストも兼ねて、今一度この本に取り組んでみようと思っています。本を読みつつ、N-Prologでの相違点など書き記して行こうと思います。N-Prologは1986年9月初版発行のRUN\PROLOGユーザーズマニュアルに基づいています。


# 相違点
◯P64
飯高先生のお使いのバージョンではinteger/1という関数があったようです。商と余りを算出する述語を次のように書いています。

```
res_q(A = B*Q + R):- Q is integer(A/B), R is A - B*Q.

```
N-Prologでは整数除算は　//　で定義されています。mod演算子もありますので、次のように書くことができます。

```
res_q(A = B*Q + R):- Q is A //B, R is A mod B.

```

以降、本を読みつつ気がついたものを書き記して行く予定です。

〇p53
SUM1/2の性能について。当時のRUN/PROLOGは640KBのPC-9801で動作していました。sum1(900,X)くらいが限界だったそうです。今ではメモリはギガバイトです。余裕で計算できます。

```
sum1(0,0).
sum1(J,S1) :- I is J-1,
              sum1(1,S),
              S1 is S + J.

    N-Prolog Ver 3.13
?- ['tests/iitaka.pl'].
yes
?- sum1(19000,X).
X = 180509500 .
yes
?- sum1(20000,X).
X = 200010000 .
yes
?-           
```

〇p146
shellは機能的にはRUN/PROLOGと同じなのですが、仕様が若干異なります。MS-DOS版ではshell/0があり、MS-DOSに制御を移してexitで戻ってきました。N-Prologではshell/0はなく、shell/1のみです。飯高先生の本を読み進めるには問題ありません。

◯p115
実行時間計測をRUN/PROLOGを使って次のようにすればよいと説明されています。

```
time(T1),call(P),time(T2),write(T1=T2).
```

N-PrologはLinuxの通常のタイマーで時刻を取得しており、秒単位までしか表示できません。

```
N-Prolog Ver 3.23
?- time(T1),time(T2),write(T1=T2).
time(9,43,42)=time(9,43,42)T1 = time(9,43,42)
T2 = time(9,43,42) .
yes
?- 

```

実行時間計測には拡張述語のmeasure/1 が使えます。実行時間を表示するとともに、1秒間あたりの推論実行回数（LIPS）を表示します。

```
?- measure(write(1)).
1Elapsed Time=0.000005 (second)  199729(LIPS)
yes
?- 
```

◯p115
RUN/PROLOGにはreverse/2は組み込みにはなっていません。そこでreverse/2を書くという例題が示されています。N-Prologにおいてはmember/2、append/3が組込述語になっています。混乱を避けるために組み込み述語を再定義しようとするとエラーになります。この場合には,mymemberあるいはmyappend のように異なる名前を使ってください。

reverse/2 はN-Prologにおいてはlistライブラリとして収録してあります。


◯p24
「注意」のところで複雑な式を再入力する際に労力を省く方法が説明されています。N-Prologの場合には「↑」を押すと以前に入力した式の履歴が出てきます。「↓」も履歴を表示することに使えます。入力が楽になります。


◯p29
?- _=3,A is _+2.
A = err ->

ユニフィケーションされていない変数に対してなんらかの演算をした場合にはN-Prologではエラーを起こします。
errアトムを返すのはオーバーフローあるいはアンダーフローを生じた場合に限られます。この場合には明示的にエラーにした方がデバッグしやすいと思います。

```
N-Prolog Ver 3.33
?- _=3,A is _+2.
Evaluation error eval  _
?- 
```

◯ｐ40
浮動小数点数の精度についてです。RUN\PROLOGの時代はコンピューターの制約から有効精度がそれほどありませんでした。したがって　?- A is log(2*3),B is log(2)+log(3),C is A - B.　で対数の定理にも関わらずCは0にはなりませんでした。しかし、現在のコンピューター上のN-Prologではdouble型を使っているので誤差はほぼ出ていません。


```
?- A is log(2*3),B is log(2)+log(3),C is A - B.
A = 0.778151
B = 0.778151
C = 0.0 .
yes
?- 

```

◯145
自家製のedit/1としてファイルをreconsultではなくconsult述語で読み込む例が紹介されています。N-Prologでは組込述語を上書きすることを禁じています。再定義による混乱を避けるためです。そこで edit/2を追加しています。edit(Filename,c) の場合にはconsultで読み込みます。rを指定した場合にはreconsultで読み込みます。redit/1はreconsultで読み込みます。 


◯p64
商と余りを求める述語
ゼロ除算を考慮していないので連分数計算でゼロ除算エラーになります。下記のように補正する必要があります。

```
%p64
res_q(A=B*Q + R) :-
        B=\=0.0,Q is integer(A/B),R is A - B*Q.
```

◯p179
ユニブの制限
本では注意としてユニブの使える複合項の引数は256以下だから・・・とあります。
N-Prologではワーキングセルの範囲で可能です。20,000,000程度は可能です。その前にパーサーのバッファがオーバーフローするかもしれません。


◯p167
写像を表すのに記号　:　を使っています。これについて定義なしに中置演算子として使っています。RUN/PROLOGではそのような定義になっていたのかもしれません。通常、Prologではそのようになっていないので、次の定義が必要に成ると思います。

```
:- op(700,xfx,:).
```

◯p171
delete1/1が使われているのですが、具体的なコードは示されていません。delete0/1を決定的にしたものと注釈がありますので、次のように成ると思われます。

```
%p171
delete1(X = [A|X]-A) :- !.
delete1([B|Y] = [B|X]-A) :- delete1(Y = X - A).

%p134
delete0(X = [A|X]-A).
delete0([B|Y] = [B|X]-A) :- delete0(Y = X - A).


```

◯p153
冪集合をもとめるのにbagof_/3が定義して使われています。N-Prologではbagof\3が組込で用意されています。
下記のようにすれば冪集合を求められます。

```
%p153
power0(Power1,Set) :-
    bagof(X,(X isl subset(Set)),Power0),
    sort(Power0,Power),
    writeln(Power),
    Power1 isl Power - [[],Set].

```

◯p167
ミスプリントだと思われます。

```
Y ism val([],X) :- !,fail. %<- failは不要
Y ism val([Y:X |F],X) :- !.
Y ism val([A|F],X) :- !,Y ism val(F,X).
```

次のような使い方になります。
```
?- Y ism val([a:1,b:2,c:3], 3).
Y = c .
yes
?- Y ism set([a:1,b:2,c:3], [1,2]).
Y = [a,b] .
yes
?- Y ism set([a:1,b:2,c:3], [1,2,3]).
Y = [a,b,c] .
yes
?- 
```

◯p168 写像の合成
おそらく誤植だと思います。正しくは下記のとおり

```
[] ism [] * G :- !.
[(Z:X)|H] ism [(X:A)|F] * G :-
        Z ism val(G,A),!,
        H ism F * G.

?- H ism [a:1,b:2] * [x:1,y:2].
H = [x:a,y:b] .
yes
?- 
```

◯p123　for_any
RUN\PROLOGのifthenelse/3を使って実装しているのですが、とてもトリッキーです。N-Prologではうまくいきません。次のように定義しなおしてください。これでうまくいきますし、シンプルで素直です。


```
/*
for_any(P,Q) :-
        P,
        ifthenelse(Q,fail,(!,fail)).
for_any(P,Q).
*/
   |
   V

for_any(P, Q) :-
    \+ (P, \+ Q).
```

これにより位相空間の計算、ptop/1が正常に動作します。

```
N-Prolog Ver 4.42
?- ['./tests/iitaka'].
yes
?- ptop([1,2,3]).
1=[[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
2=[[],[1],[2],[1,2],[1,3],[1,2,3]]
3=[[],[1],[2],[1,2],[2,3],[1,2,3]]
4=[[],[1],[2],[1,2],[1,2,3]]
5=[[],[1],[3],[1,2],[1,3],[1,2,3]]
6=[[],[1],[3],[1,3],[2,3],[1,2,3]]
7=[[],[1],[3],[1,3],[1,2,3]]
8=[[],[1],[1,2],[1,3],[1,2,3]]
9=[[],[1],[1,2],[1,2,3]]
10=[[],[1],[1,3],[1,2,3]]
11=[[],[1],[2,3],[1,2,3]]
12=[[],[1],[1,2,3]]
13=[[],[2],[3],[1,2],[2,3],[1,2,3]]
14=[[],[2],[3],[1,3],[2,3],[1,2,3]]
15=[[],[2],[3],[2,3],[1,2,3]]
16=[[],[2],[1,2],[2,3],[1,2,3]]
17=[[],[2],[1,2],[1,2,3]]
18=[[],[2],[1,3],[1,2,3]]
19=[[],[2],[2,3],[1,2,3]]
20=[[],[2],[1,2,3]]
21=[[],[3],[1,2],[1,2,3]]
22=[[],[3],[1,3],[2,3],[1,2,3]]
23=[[],[3],[1,3],[1,2,3]]
24=[[],[3],[2,3],[1,2,3]]
25=[[],[3],[1,2,3]]
26=[[],[1,2],[1,2,3]]
27=[[],[1,3],[1,2,3]]
28=[[],[2,3],[1,2,3]]
29=[[],[1,2,3]]
no
?- 

```