このディレクトリの索引

#  
#  Prologという宣言型プログラミング言語にふれる。ルールを定義するという点を除くと、
#  確かにSQL(データに対しこれ欲しいみたいなクエリ)に近しい感じがします。
#  ということで、アクセスパス(性能)が気になるのは、命令型に侵されてるからかな?

%  
%  Prologの述語の定義節の間では基本的に情報の授受ができません。
%  具体的に示しましょう。
%  

成績(阿部,70).
成績(尾崎,55).
成績(山田,90).

% と定義されている時、三人の平均点を取りたいとします。(集約問題)

?- 成績(_,_成績).
_ = 阿部,
_成績 = 70;
_ = 尾崎,
_成績 = 55;
_ = 山田,
_成績 = 90;
false

%  というように取得できますが、_ = _尾崎,_成績 = 55が取得できた時には阿部の
%  情報は変数の束縛を開放してしまっていますから、利用できません。
%  個々の節が全く独立しているわけですから合計していくことができないことになります。
%  ただし、以下のように情報が連鎖するように仕組めば可能になります。
%  

成績(阿部,尾崎,70).
成績(尾崎,山田,55).
成績(山田,阿部,90).

成績の平均(_起点,_平均点) :-
        成績(_起点,_生徒,_),
        成績の合計(_起点,_生徒,_人数,_合計),
        _平均点 is _合計 / _人数.

成績の合計(_起点,_起点,1,_成績) :-
        成績(_起点,_,_成績),!.
成績の合計(_起点,_生徒_1,_人数,_合計) :-
        成績(_生徒_1,_生徒_2,_成績),
        成績の合計(_起点,_生徒_2,_人数_2,_合計_2),
        _合計 is _合計_2 + _成績,
        _人数 is _人数_2 + 1.

%  と一応はできました。
%  実は集約するために必要な条件は情報の連鎖なのです。
%  Prologで扱う連鎖構造の代表にリストがあります。
%  ここでも、[70,55,90]というリストが欲しい。
%  成績という述語の節の中にある成績(点数)だけを抜き出してリストとしたい。
%  これを実現するfindallというメタ述語がPrologには用意されています。

成績(阿部,70).
成績(尾崎,55).
成績(山田,90).

成績リスト(_成績リスト) :-
        findall(_成績,成績(_,_成績),_成績リスト).

?- 成績リスト(_成績リスト).
_成績リスト = [70,55,90];
false

%  と成ります。
%  
%  このfindallこそ SQL にそっくりですね。
%  
%  assert/retractを繰り返しながらリストを成長させて行くほかない述語で
%  通常のPrologのスタイルからは外れた述語です。メタ述語です。
%  

成績の平均(_平均点) :-
        成績リスト(_成績リスト),
        sum_list(_成績リスト,_合計),
        length(_成績リスト,_人数),
        _平均点 is _合計 / _人数.

%  sum_listは組込述語になっていますが、定義するとしたら、

sum_list([],0).
sum_list([N|R],_合計) :-
        sum_list(R,_合計_2),
        _合計 is _合計_2 + N.