また問題を少し変えて, こんどは与えられた数未満の自然数の平方と立方の両 方の総和を計算するプログラムを考えてみよう.
前節の場合と同様の方法で, 入力 n に対して
を出力とするよう
なフィルタを作るという方法はある. しかし, それではせっかく作った
square 述語は使わずに, 別に定義しなければならない. そこで, 前述の
square はそのままにして, 別に入力メッセージの立方を出力するようなフィ
ルタを作って, これを使うことを考える.
このようなフィルタ自体をどう定義すれば良いかは, もうおわかりだろう.
cube([],Out) :- Out=[].
cube([One|Rest],Out) :-
Cube:=One*One*One, Out=[Cube|OutTail], cube(Rest,OutTail).
このふたつのフィルタと残りの naturals, sum をどうストリームで結合する かだが, 入力には両方ともプロセス naturals の出力を共通に与えれば良い. これはごく簡単で
square_sum_up_to(N,Sum) :-
naturals(N,Naturals), square(Naturals,Squares), cube(Naturals,Cubes),
...
と, 同じ変数を両方に書けば良い. 問題は Squares, Cubes の2本の出力スト
リームに流れるメッセージすべてを, どうやってプロセス sum に渡すかであ
る.
ひとつの方法は, まず片方のストリームのメッセージを送り込み, それが終っ たらもう一方のストリームのメッセージを送るようにすることである. その ための交通整理をする述語は以下のように書ける.
この述語は第1, 第2のふたつの引数が入力ストリームに, 第3引数が出力スト リームになっている. 全体としては, まず第1引数にやってくるメッセージを 次々に出力し, それが終ったら第2引数の方のメッセージを流すようにしてい る.append([],In2,Out) :- Out=In2. append([Msg|In1],In2,Out) :- Out=[Msg|OutTail], append(In1,In2,OutTail).
最初の節は, 第1引数である一方のストリームが終りまで来たら, 後は第2引数
であるもう一方のストリームをそのまままとめて出力ストリームとしてしまえ
ば良い, という意味である. メッセージをひとつひとつ取り出しては中継す
る必要はないわけである. もうひとつの節は, 第1引数のストリームの方にメッ
セージが来たら, それをそのまま出力することを意味する.
この述語を使って全体のプログラムを書くと, 以下のようになる.
queer_sum(N,Sum) :-
naturals(N,Naturals), square(Naturals,Squares), cube(Naturals,Cubes),
append(Squares,Cubes,Both), sum(Both,Sum).
メッセージを交通整理する append では, メッセージの中身はまったく見てい ない. 単にメッセージが来たかどうかだけを見て, 来たメッセージをそのま ま中継しているだけである. そのため, どんなメッセージが流れるストリー ムに対しても, 同じ append を使うことができる. KL1 の変数に型がない (どんな型の値も入れられる) ことの利点が現れる例である.