複素数は,実数の対で表すことができますから, Prologのプログラム中では,次のような長さ2のリストで表すことにします.
[ 実数部 , 虚数部 ]したがって,まずは,次のような述語 c を定義することが目標になります. 述語 c は,第1引数に式を受けとって,第2引数に計算結果の複素数を返します.
?- c((1+2*i)*(3+4*i), C). C = [-5,10] ?式は再帰的な構造を持っていますから,述語 c も再帰的に定義する必要が あります. つまり,c(X, C) を次のように定義します.
c(X, C) :- number(X), % X が実数の時 C = [X,0]. % C は [X,0] c(i, C) :- % X が虚数単位 i の時 C = [0,1]. % C は [0,1] c(X1+X2, C) :- % X が X1+X2 の時 c(X1, [R1,I1]), % X1 を再帰的に計算 c(X2, [R2,I2]), % X2 を再帰的に計算 R is R1 + R2, I is I1 + I2, C = [R,I]. c(-X1, C) :- % X が -X1 の時 省略. c(X1-X2, C) :- % X が X1-X2 の時 省略. c(X1*X2, C) :- % X が X1*X2 の時 省略. c(X1/X2, C) :- % X が X1/X2 の時 省略.これで,述語 c が定義できたわけですが,使い方が電卓らしくありません. 次のようにユーザが入力できるようにしましょう.
?- c. % プログラムを起動 |: (1+2*i)*(3+4*i). % ユーザが入力 (ピリオドが必要!) -5+10*i % 計算結果が表示されるユーザからの入力は述語 read で読み込むことができます.
c :- read(X), % ユーザの入力を読む c(X, [R,I]), % 計算 write(R+I*i), % 結果を表示 nl. % 改行を出力でも,これだと一回しか入力を読まないので, c を再帰呼出しして,繰り返し計算するように変更します.
c :- read(X), % ユーザの入力を読む c(X, [R,I]), % 計算 write(R+I*i), % 結果を表示 nl, % 改行を出力 c. % 繰り返し繰り返しを終了するには,Ctrl-C を入力 (MuleのshellからはCtrl-C Ctrl-Cを入力)してから,a を入力してください.
Ctrl-C Prolog interruption (h for help)? a {Execution aborted} | ?-
これで,一応,複素数電卓の完成ですが, まだ改良すべき点が残っています. たとえば,i を入力すると,0+1*i と表示され, -i を入力すると,0+ -1*i と表示されてしまいます. より自然な表示が行なわれるように改良して下さい.
また,全く別の改良も考えられます. 変数を使えるようにする改良です. たとえば下の使用例では,変数 x に値を代入して,その値を利用しています.?- c. |: x = (1+2*i)*(3+4*i). % 変数 x に代入 -5+10*i |: x + x. % 変数 x の値を利用 -10+20*i変数名とその値を記憶しておくには,assertを用います. assertについては,教科書「Prologへの入門」の7章を参照して下さい.
:- dynamic val/2. % valを assert することの宣言 % プログラムの先頭に置く c(X, C) :- atom(X), % X が変数名(記号)の時 value(X, C). % C は変数 X の値 c(V = X, C) :- % V = X の時 c(X, C), 省略. % 変数 V に 値 E を代入する assign(V, E) :- retractall(val(V, _)), % valの節があれば,すべて削除 assert(val(V, E)). % valの節をassert % 変数 V の 値 E を返す value(V, E) :- val(V, E), % assertされていればその値 !. value(_, [0,0]). % assertされていなければ [0,0]