% Illustrating difference lists % Author: Frank Pfenning % Oct 2006, following Sterling & Shapiro, The Art of Prolog % support for polymorphic type checking :- op(1200, xfx, ':'). :- discontiguous([(:)/2]). :- dynamic([(:)/2]). % support for queues :- op(200, xfy, '\\'). :- dynamic([queue/1,q/2]). :- dynamic([reverse_/2, rev/2]). list : [type] -> type. [] : [] -> list(_A). '.' : [A, list(A)] -> list(A). inst : [type] -> type. enq : [A] -> inst(A). deq : [A] -> inst(A). queue : [list(inst(_A))] -> o. qu : [type] -> type. (\) : [list(A),list(A)] -> qu(A). q : [list(inst(A)),qu(A)] -> o. /* This first version of queues allows ``borrowing'' items from the future, that is, we can remove virtual elements from the queue and only later fill it. ?- queue([deq(X),enq(1)]). succeeds with X = 1. */ queue(Is) :- q(Is, B\B). q([enq(X)|Is], F\[X|B]) :- q(Is, F\B). q([deq(X)|Is], [X|F]\B) :- q(Is, F\B). q([], []\[]). /* to guard against negative queues replace middle clause by q([deq(X)|Is], F\B) :- F = [] -> fail ; F = [X|F1], q(Is, F1\B). */ /* This second version prevents time travel by carrying queue size. Time travel can also be avoided as indicated above. ?- queue([deq(X),enq(1)]). fails */ queue2(Is) :- q2(Is, Q\Q, 0). q2([enq(X)|Is], F\[X|B], N) :- N1 is N+1, q2(Is, F\B, N1). q2([deq(X)|Is], [X|F]\B, N) :- N > 0, N1 is N-1, q2(Is, F\B, N1). q2([], []\[], 0). /* This strawman version is extremely inefficient because of repeated appends */ queue0(Is) :- q0(Is, []). q0([enq(X)|Is], Q) :- append(Q, [X], Q2), q0(Is, Q2). q0([deq(X)|Is], [X|Q]) :- q0(Is, Q). q0([], []). /* This strawman version is a translation of functional queues into Prolog, not as elegant as the difference list version above. */ queue1(Is) :- q1(Is, [], []). q1([enq(X)|Is], F, B) :- q1(Is, F, [X|B]). q1([deq(X)|Is], [X|F], B) :- q1(Is, F, B). q1([deq(X)|Is], [], B) :- reverse(B, [X|F]), q1(Is, F, []). q1([], [], []). reverse_ : [list(A),list(A)] -> o. rev : [list(A),qu(A)] -> o. reverse_(Xs, Ys) :- rev(Xs, Ys\[]). rev([X|Xs], Ys\Zs) :- rev(Xs, Ys\[X|Zs]). rev([], Ys\Ys). /* Naive version, often used for benchmarking purposes. */ naive_reverse([X|Xs], Zs) :- naive_reverse(Xs, Ys), append(Ys, [X], Zs). naive_reverse([], []). % quicksort not type-checked: need to extend checker % to allow =< and > and ! quicksort(Xs, Ys) :- qsort(Xs, Ys\[]). qsort([], Ys\Ys). qsort([X0|Xs], Ys\Zs) :- partition(Xs, X0, Ls, Gs), qsort(Gs, Ys2\Zs), qsort(Ls, Ys\[X0|Ys2]). partition([], _, [], []). partition([X|Xs], X0, [X|Ls], Gs) :- X =< X0, partition(Xs, X0, Ls, Gs). partition([X|Xs], X0, Ls, [X|Gs]) :- X > X0, partition(Xs, X0, Ls, Gs).