差分リストは、むしろ別の制限を回避するために使用されます: リスト全体をトラバースして最後に「追加」する必要があります (最初の要素へのポインターを持ち、センチネルへのポインターを持たない単一リンクリストを考えてください)。
コード内: リスト[a, b, c]
は (伝統的に) ネストされた term.(a, .(b, .(c, [])))
であるため、その末尾に a を追加することは、末尾 (中央) の をd
に置き換える前に各用語を「剥がす」ことを意味します。したがって、実装するには:[]
.(d, [])
append/3
append([], L, L).
append([H|T], L, [H|R]) :-
append(T, L, R).
これが伝統的に実装されている方法です。
これは、トピックを非常に広範囲にカバーする別の回答です。
その答えが入っていないのは、リストの最後に追加したい場合に備えて、効率上の理由から、リストを「返す」ライブラリ述語が述語の差分リストバージョンを提供する可能性があるということです。例はread_pending_codes/3
and read_pending_chars/3
、または の 4 引数バージョンですfindall/4
。
DCG は、リストと末尾の 2 つの引数を明示的に渡すことなく、差分リストを操作する便利な方法です。
キューの実装
キューの 3 つの最も基本的な操作: 空のキューの作成、後方へのプッシュ、前方からのポップ:
%% empty_queue(-Queue)
% make an empty queue
empty_queue(queue(0, Q, Q)).
%% queue_head(?Queue, ?Head, ?Queue0)
% Queue, with Head removed, is Queue0
queue_head(queue(s(X), [H|Q], Q0), H, queue(X, Q, Q0)).
%% queue_last(+Queue0, +Last, -Queue)
% Queue0, with Last at its back, is Queue
queue_last(queue(X, Q, [L|Q0]), L, queue(s(X), Q, Q0)).
お気づきのように、queue_head/3
前からポップしたり、キューの前にプッシュしたりできます。queue_last/3
後ろにプッシュすることしかできません。要素をプッシュすると、キュー内のその前の要素に(一定時間)アクセスできなくなります。
この用語の最初の引数は、queue/3
Richard O'Keefe が「幻覚的」変数と呼んでいるもの、つまり、キューにプッシュされたよりも多くの要素をキューからポップすることを防ぐためのものです。上記の述語から最初の引数を除外して、何が起こるかを確認するのは興味深いことです。
?- empty_queue(Q0), queue_head(Q0, H, Q1).
Q0 = queue([H|_G341], [H|_G341]),
Q1 = queue(_G341, [H|_G341]).
失敗する代わりに。