算術
既に発見したように、算術演算は Prolog で演算子を使用して処理できます(is)/2
。これは、Prolog では、すべてが記号計算にすぎないためです。デフォルトでは、物事には意味がないため、たとえば加算を参照して(=)/2
いることを統合は認識しません。(+)/2
さて、あなたの問題は、内部で通常の述語を使用することです(is)/2
(ここでは再帰呼び出しです)。算術のみを実行するため(is)/2
、述語呼び出しを評価しません。算術関数ではないため、認識さえしません。
ここでの修正は、変数への再帰呼び出しの結果に影響を与え、それを(is)/2
呼び出しで使用することです。
listsum(X,[]).
listsum(Result, [Head|Tail]) :-
listsum(SumOfTail, Tail),
Result is Head + SumOfTail.
ベースケースの正確さ
しかし、そのコードをテストしても、望ましい結果は得られません。その理由は、基本ケースで別の問題があるためです。あなたが書いたように、空のリストの合計は「何も」ではありません
listsum(X,[]).
(X
は自由変数なので、何でもかまいません)。
代わりに、次の0
とおりです。
listsum(0, []).
結果のコードは次のとおりです。
listsum(0, []).
listsum(Result, [Head|Tail]) :-
listsum(SumOfTail, Tail),
Result is Head + SumOfTail.
引数の順序
さて、補足として、Prolog では、出力変数は述語の最後に配置し、入力変数は述語の最初に配置するという慣習があるため、必要に応じて動作させるには、次のようにリファクタリングできます。
listsum([], 0).
listsum([Head|Tail], Result) :-
listsum(Tail, SumOfTail),
Result is Head + SumOfTail.
テールコールの最適化
現在、この述語はさらに高度な手法で改善できます。たとえば、アキュムレータと呼ばれる宣言型プログラミングのイディオムのおかげで、テール コールの最適化 (googlable) を実行できるように、テール コールを導入できます。
listsum(List, Sum) :-
listsum(List, 0, Sum).
listsum([], Accumulator, Accumulator).
listsum([Head|Tail], Accumulator, Result) :-
NewAccumulator is Accumulator + Head,
listsum(Tail, NewAccumulator, Result).
その背後にある考え方は、再帰の各ステップで (リストの現在の先頭の値を追加することによって) 中間結果を更新し、リストが空の場合、この中間値が最終値であることを述べるだけです。
より一般的なプログラムを取得する
Prolog で指摘したように、述語はさまざまな方法で使用できることがよくあります。たとえばlength/2
、リストの長さを検出するために使用できます。
?- length([1, 2, 3], Length).
Length = 3.
または、必要な長さの自由変数を使用してスケルトン リストを作成するには、次のようにします。
?- length(List, 3).
List = [_G519, _G522, _G525].
ただし、ここで、合計が であるリストを Prolog に尋ねることはできないことに気付いたかもしれません6
。
?- listsum(L, 6).
ERROR: is/2: Arguments are not sufficiently instantiated
(is)/2
これは、「逆方向に進む」ために、Prolog はオペレーターへの呼び出し時に方程式を解かなければならないためです。そして、あなたのものは単純ですが(足し算のみ)、算術は一般的なケースではこの方法では解決できません。
この問題を解決するために、制約プログラミングを使用できます。SWI 用の非常に優れたライブラリである clpfd が利用可能です。
ここでの構文は次のようになります。
:- use_module(library(clpfd)).
listsum(List, Sum) :-
listsum(List, 0, Sum).
listsum([], Accumulator, Accumulator).
listsum([Head|Tail], Accumulator, Result) :-
NewAccumulator #= Accumulator + Head,
listsum(Tail, NewAccumulator, Result).
これで、述語を別の方法で使用できるようになりました。
?- listsum(L, 6).
L = [6] ;
L = [_G1598, _G1601],
_G1598+_G1601#=6 ;
L = [_G1712, _G1715, _G1718],
_G1712+_G1715#=_G1728,
_G1728+_G1718#=6 . % Here I interrupted the answer but it would not terminate.
問題のすべての解決策を尋ねることもできます。
?- listsum(L, X).
L = [],
X = 0 ;
L = [X],
X in inf..sup ;
L = [_G2649, _G2652],
_G2649+_G2652#=X . % Here I interrupted the answer but it would not terminate
(is)/2
最も一般的なプログラムを取得するには、 の使用を避けるべきであり、制約プログラミングの使用を優先する必要があることがよくあることを理解できるように、先ほど述べました。