Prologプログラミング: 複素数電卓を作ろう

Last modified: Wed Aug 29 22:36:04 2001 JST

ここでは,複素数電卓を作ってみます.

複素数は,実数の対で表すことができますから, Prologのプログラム中では,次のような長さ2のリストで表すことにします.


        [ 実数部 , 虚数部 ]
したがって,まずは,次のような述語 c を定義することが目標になります. 述語 c は,第1引数に式を受けとって,第2引数に計算結果の複素数を返します.

    ?- c((1+2*i)*(3+4*i), C).
    C = [-5,10] ?
式は再帰的な構造を持っていますから,述語 c も再帰的に定義する必要が あります. つまり,c(X, C) を次のように定義します. これをPrologのプログラムに直すと以下のようになります.

    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]

Naoyuki Tamura