いろいろなプログラム
Prologのプログラムの書き方やインタプリタの動作の仕方が大体判ってきたと思います。それでは、ぼちぼち簡単なプログラムを作成してみましょう。先ず、小さなプログラムをいくつか示します。これらがどのように動くのか解析しましょう。続いて、演習問題に取り組んでください。
%
% problem01.pl
%
% 与えた数が4以下の正の整数かどうかを判定する
num_1234(N):-N=:=1.
num_1234(N):-N=:=2.
num_1234(N):-N=:=3.
num_1234(N):-N=:=4.
Prologでは、数の比較には=:=を用います。詳しいことは算術計算の章で学習することとして、ここでは
を用いる、ということだけ記憶に留めおいてください。
上のproblem01.plは=:=の例として出してみたものです。実際のPrologのプログラムでは、これは以下のようにするのが一般的です。
num_1234(1).
num_1234(2).
num_1234(3).
num_1234(4).
%
% problem02.pl
%
% 与えた4整数を加算する
sum_four(N1,N2,N3,N4,Z):-
X is N1+N2,
Y is N3+N4,
Z is X+Y.
変数に数値を代入する場合は、is を使います。
%
% problem03.pl
%
% 与えた数が整数かどうかを判定する
positive(N):-N>0.
%
% problem04.pl
%
% 与えたリストの要素を合計する
sum_few([N1,N2,N3,N4],Z):-
sum_four(N1,N2,N3,N4,Z).
sum_few([N1,N2,N3],Z):-
X is N1+N2,
Z is X+N3.
sum_few([N1,N2],Z):-
Z is N1+N2.
sum_few([N1],N1).
sum_few([],0).
problem04.pl では、problem02.pl の sum_four を利用しています。このような場合、これらのプログラムを1つのファイルに作成してもいいですが、それぞれ別のファイルに作成しておいてPrologインタプリタに読み込むときに2つのファイルを続けて読み込んでも構いません。
%
% problem05.pl
%
% リストの要素が全てゼロであることを確認する
zeros([0]).
zeros([0|Tail]):-
zeros(Tail).
リストとは、データの列です。Prologでは[ ]を使って次のように書きます。
[1,2,3,4,5] [a,aa,aaa,aaaa] [one,two,three]
リスト・データに関するユニファイで注意しなければならないのは[0 | Tail]などの縦棒です。これはリストを先頭の要素と残りのリストに分けます。例えば、
[Head|Tail]
といったときに、上のデータそれぞれは
[1,2,3,4,5]であれば、Head=1,Tail=[2,3,4,5]
[a,aa,aaa,aaaa]であれば、Head=a,Tail=[aa,aaa,aaaa]
[one,two,three]であれば、Head=one,Tail=[two,three]
となります。縦棒の左側の変数は単独のデータ、右側はリストとなる点に注意してください。
%
% problem06.pl
%
% リストの要素が全て正数であることを確認する
pos([]).
pos([Head|Tail]):-
Head>0,pos(Tail).
%
% problem07.pl
%
% リストの要素の総和を計算する
total([],0).
total([Head|Tail],Sum):-
total(Tail,SubSum),
Sum is SubSum + Head.
%
% problem08.pl
%
% 始値から終値までの整数のリストを作成する
integers(From,From,[From]).
integers(From,To,[From|Tail]):-
From > To,
NewFrom is From+1,
integers(NewFrom,To,Tail).
problem08.plでは、例えば
?- integers(1,5,X).
というように質問します。Prologの答えは、
X = [1,2,3,4,5]
のようになります。
%
% problem09.pl
%
% リストのビット反転を行う
% 0は1に、1は0に反転し、0、1以外は?とする
bit([],[]).
bit([0|In],[1|Out]):-
bit(In,Out).
bit([1|In],[0|Out]):-
bit(In,Out).
bit([_|In],[?|Out]):-
bit(In,Out).
%
% problem10.pl
%
% 簡単な記憶プロセスを実現する
% 第2パラメータをスタックメモリと見なし、
% pushでデータを積み、popで吐き出す
stuck([push(Data)|Tail],Mem):-
stuck(Tail,[Data|Mem]).
stuck([pop(X)|Tail],[]):-
X = 'Sorry, I am empty',
stuck(Tail,[]).
stuck([pop(X)|Tail],[Data|Mem]):-
X = Data,
stuck(Tail,Mem).
stuck([],_).
problem10.plは少し判りにくいかもしれません。これはアセンブラのpush/popを思い出してください。例えば、
?- stuck([push(a),push(b),pop(X),pop(Y)],[]).
X = b
Y = a
となります。メモリの状態を[ ]、即ち空としておいて、push(a),push(b)で下からa、bとデータを積み上げます。そして、pop(X)とすると変数Xにはbが代入されます。スタックは「先入れ後出し」です。実行時にトレースを指定しておくともっと判りやすいかもしれません。第2パラメータの部分がpushを処理することにより[ ]から[a]となり、[b,a]となります。更にpopを処理することにより[b,a]から[a]となり、[ ]になります。これはメモリ上にデータを積み上げていくスタック処理を想定しているのです。
《演習問題》
- 与えられた2数が等しいかどうかを判定する。
- 与えられた2数の差を計算する。この際、差は正数で答えること。
- 与えられた正数が偶数かどうかを判定する。
ある数が偶数かどうかはどのようにして判断したらよいのでしょうか。2で割った余り(剰余)を用いるのが最もスマートです。が、剰余は算術計算の章で勉強することとしましょう。ここでは、どんどん2を引いていって、最終的にゼロになったら偶数、そうでなかったら奇数と考えてください。
- 与えられた正数が奇数かどうかを判定する。
- 与えられた正数のリストの要素が全て偶数かどうかを判定する。
- 与えられた正数のリストの要素が全て奇数かどうかを判定する。
- 始値から始めて終値を超えない範囲で3の倍数のリストを作成する。
これは結構難しいと思います。最初にある数が3の倍数かどうか判断する関数を作りましょう。次にproblem08.plを参考に3の倍数のリストを作る関数を定義します。そして、リストの要素の生成を指定の値を超えない3の倍数で止める、ということについて考えてみましょう。
閑話休題
ELIZAを作ったのはMITのジョセフ・ワイゼンバウムです。ワイゼンバウムは全然知的でないELIZAを精神分析医やユーザが絶賛するのを見て、次のように語っています。
人間は簡単にコンピュータにだまされてしまい、非人間的なマシンを過度に信頼し、その振る舞いを客観的に評価できない。したがって、コンピュータは人間社会から隔離された特定の領域に封じ込めるべきである。
例えば、人工知能プログラムが裁判で判事や陪審員として使われたらどうなるかを考えてみればよい。適切にプログラムされれば、コンピュータは法律的な問題を十分に理解し、また生成でき、大規模なデータベースに瞬時にアクセスして過去の判例や法律を検索し、証拠に対する公正で偏りのない判断を下せるだろう。そこには人種差別や性差別などの偏見があってはならず、また法を越えた要因によって偏向してはいけない。このような展望は多くの人々にとって理想的に見えるかもしれないが、しかし、権威と権力を基本的に生命のない"エイリアン"の手にゆだねることの危険性を考えるべきである。例えば、コンピュータは"虐殺"という言葉を殺された人間の数という文脈でなら理解するかもしれないが、人間の苦悩という歴史的且つ感情的な文脈では理解できないのである。
目次に戻る