Related
I have a simple problem, and I know why what I'm doing is wrong, I just dont know how to do what I want.
I want to be able to find the max of list X and then append it to the result Xs. It would recurse slowly sorting the list until it was finished. I know there are better sorting algorithms but I want to try and make my problem work.
These are the 2 version of the code that I have tried but I seem to be getting no where
mysort([],_).
mysort([X,Y], Xs) :- max(X,M), mysort(Y,[M|Xs]).
mysort([],_).
mysort([X,Y], Xs) :- max(X,M),append(Xs, [M], Xs) mysort(Y,[M|Xs]).
I would hope to have some kind of hint to completing this code, or someone fixing this silly problem.
I am not sure if I understood your question correctly, but maybe you can use some of this:
Example one:
mysort(List, Result) :-
aggregate(max(X), member(X, List), Result).
Result:
mysort([1,2,3,4], Result).
Result = 4
Example two:
mysort(List, MaxList, Result) :-
aggregate(max(X), member(X, List), Max),
append(MaxList, [Max], Result).
Result:
mysort([1,2,300,3], [100, 200], Result)
Result = [100, 200, 300]
Example three:
mysort(List, Result) :- mysort(List, [], Result).
mysort([], Result, Result).
mysort([H | T], Tmp, Result) :-
aggregate(max(X), member(X, H), Max),
TmpResult = [Max | Tmp],
mysort(T, TmpResult, Result).
Result:
mysort([[122,2,3,4, 6, 10], [127,2,3,4, 6, 10], [127,2,3,4, 6, 10]], Result).
Result = [127, 127, 122]
[X,Y] is a list of 2 elements, then is not what you want.
Instead, you should concentrate on building a predicate select_max/3, that 'extracts' the maximal value and returns it and the list without it.
We could code it adapting the select/3 library predicate:
select_max(Max,List,Rest) :-
select(Max,List,Rest),
\+ (member(Other,List),Other#>Max).
Now you can try to build the main recursive mysort/2, I will left to you as an exercise (remember that the base case should instantiate the output list to empty).
First, you need a predicate to select a maximum element, separating it from the rest of the list. To do so:
Start by taking the first element of the list [X|Xs] as the current maximum element.
Then, recursively traverse the rest of the list (Xs) by comparing the current maximum element (X0) to the current first element (X1), separating the biggest of the two and putting the other in the rest.
Finally, when the list is empty, the current maximum element (Max) is indeed the maximum element of the original list.
% select_max(-Max, +List, -Rest)
select_max(Max, [X|Xs], Rest) :-
select_max(Xs, X, Max, Rest).
select_max([], Max, Max, []).
select_max([X1|Xs], X0, Max, [Lesser|Rest]) :-
compare(Relation, X0, X1),
sorted(Relation, X0, X1, Lesser, Bigger),
select_max(Xs, Bigger, Max, Rest).
sorted(=, A, B, A, B).
sorted(<, A, B, A, B).
sorted(>, A, B, B, A).
Example:
?- trace(select_max, +all), select_max(M, [5,3,7,1], R), trace(select_max, -all).
% select_max/3: [all]
% select_max/4: [all]
T [11] Call: select_max(_30924434, [5, 3, 7, 1], _30924438)
T [20] Call: select_max([3, 7, 1], 5, _30924434, _30924438)
T [29] Call: select_max([7, 1], 5, _30924434, _30929268)
T [38] Call: select_max([1], 7, _30924434, _30930168)
T [47] Call: select_max([], 7, _30924434, _30931068)
T [47] Exit: select_max([], 7, 7, [])
T [38] Exit: select_max([1], 7, 7, [1])
T [29] Exit: select_max([7, 1], 5, 7, [5, 1])
T [20] Exit: select_max([3, 7, 1], 5, 7, [3, 5, 1])
T [11] Exit: select_max(7, [5, 3, 7, 1], [3, 5, 1])
% select_max/3: Not tracing
% select_max/4: Not tracing
M = 7,
R = [3, 5, 1].
Afterwards, you should use predicate select_max/3 to define your sorting algorithm (aka. Bubble Sort) as follows:
% mysort(+List, -SortedList)
mysort(List, SortedList) :-
mysort(List, [], SortedList).
mysort([], SortedList, SortedList).
mysort([X|Xs], SortedSuffix, SortedList) :-
select_max(Max, [X|Xs], Rest),
mysort(Rest, [Max|SortedSuffix], SortedList).
Example:
?- trace(mysort, +all), mysort([5,3,7,1], S), trace(mysort, -all).
% mysort/2: [all]
% mysort/3: [all]
T [11] Call: mysort([5, 3, 7, 1], _36832)
T [20] Call: mysort([5, 3, 7, 1], [], _36832)
T [29] Call: mysort([3, 5, 1], [7], _36832)
T [38] Call: mysort([3, 1], [5, 7], _36832)
T [47] Call: mysort([1], [3, 5, 7], _36832)
T [56] Call: mysort([], [1, 3, 5, 7], _36832)
T [56] Exit: mysort([], [1, 3, 5, 7], [1, 3, 5, 7])
T [47] Exit: mysort([1], [3, 5, 7], [1, 3, 5, 7])
T [38] Exit: mysort([3, 1], [5, 7], [1, 3, 5, 7])
T [29] Exit: mysort([3, 5, 1], [7], [1, 3, 5, 7])
T [20] Exit: mysort([5, 3, 7, 1], [], [1, 3, 5, 7])
T [11] Exit: mysort([5, 3, 7, 1], [1, 3, 5, 7])
% mysort/2: Not tracing
% mysort/3: Not tracing
S = [1, 3, 5, 7].
More examples:
?- mysort([], S).
S = [].
?- mysort([5], S).
S = [5].
?- randseq(5, 100, L), mysort(L, S).
L = [77, 68, 11, 38, 65],
S = [11, 38, 65, 68, 77].
?- randseq(7, 100, L), mysort(L, S).
L = [57, 5, 16, 8, 11, 53, 55],
S = [5, 8, 11, 16, 53, 55, 57].
I wrote this prolog program to find all possible paths in a grid:
travel([X,Y],[X,Y1]) :- Y1 is Y+1.
travel([X,Y],[X,Y0]) :- Y0 is Y-1.
travel([X,Y],[X1,Y]) :- X1 is X+1.
move([X,Y],n,[X,Y1]) :- travel([X,Y],[X,Y1]).
move([X,Y],s,[X,Y0]) :- travel([X,Y],[X,Y0]).
move([X,Y],e,[X1,Y]) :- travel([X,Y],[X1,Y]).
safe([Xn,Yn],[Xg,Yg]) :-
Xg >= Xn,
Xn >= 0,
Yg >= Yn,
Yn >= 0. %next state should be whit-in grid
%% solve([X,Y],[TargetX,TargetY],[Xg,Yg],[FirstMove|OtherMoves])
solve([X,Y],[X,Y],_,[]).
solve([X,Y],[Xt,Yt],[Xg,Yg],[Fm|Om]) :-
move([X,Y],Fm,[Xn,Yn]),
safe([Xn,Yn],[Xg,Yg]),
solve([Xn,Yn],[Xt,Yt],[Xg,Yg],Om).
For solve, [X,Y] is the current position. So my ending state is when the current position equals to target position. However, when I run it, I got out of memory error. Any idea what I did wrong? Any help is appreciated!
?- solve([1,2],[4,2],[3,4],P).
ERROR: Stack limit (1.0Gb) exceeded
ERROR: Stack sizes: local: 0.5Gb, global: 0.4Gb, trail: 29.0Mb
ERROR: Stack depth: 951,746, last-call: 0%, Choice points: 1,903,475
ERROR: Possible non-terminating recursion:
ERROR: [951,746] user:solve([length:2], [length:2], [length:2], _114212638)
ERROR: [951,745] user:solve([length:2], [length:2], [length:2], [length:1|_114212704])
?- length(P,4),solve([1,2],[4,2],[3,4],P).
false.
?- length(P,5),solve([1,2],[4,2],[3,4],P).
false.
Your program is infinitely looping between north and south movements. Try removing the south clauses in move and travel and it will work.
To debug how this is happening try using trace and see a recursive calls to solve you can see what is going on.
Exit: (15) move([1, 3], n, [1, 4]) ?
Call: (15) safe([1, 4], [4, 4]) ? s
Exit: (15) safe([1, 4], [4, 4]) ?
Call: (15) solve([1, 4], [3, 4], [4, 4], _3490) ?
Call: (16) move([1, 4], _3804, [_3822, _3828]) ? s
Exit: (16) move([1, 4], n, [1, 5]) ?
Call: (16) safe([1, 5], [4, 4]) ? s
Fail: (16) safe([1, 5], [4, 4]) ?
Redo: (16) move([1, 4], _3804, [_3822, _3828]) ? s
Exit: (16) move([1, 4], s, [1, 3]) ?
Call: (16) safe([1, 3], [4, 4]) ? s
Exit: (16) safe([1, 3], [4, 4]) ?
Call: (16) solve([1, 3], [3, 4], [4, 4], _3806) ?
Call: (17) move([1, 3], _4326, [_4344, _4350]) ? s
Exit: (17) move([1, 3], n, [1, 4]) ?
Call: (17) safe([1, 4], [4, 4]) ? s
Exit: (17) safe([1, 4], [4, 4]) ?
Call: (17) solve([1, 4], [3, 4], [4, 4], _4328) ?
Call: (18) move([1, 4], _4642, [_4660, _4666]) ? s
Exit: (18) move([1, 4], n, [1, 5]) ?
Call: (18) safe([1, 5], [4, 4]) ? s
Fail: (18) safe([1, 5], [4, 4]) ?
Also you seem to be pattern matching variable names in move :- travel, which will also not work. move(P1, n, P2) will try north and south clauses not just the first one(try move([2, 2], s, X) and see that the first solution is north movement). This will work but with south clause you will have infinite recursion.
move([X,Y], n, [X,Y1]) :- Y1 is Y+1.
move([X,Y], s, [X,Y1]) :- Y1 is Y-1.
move([X,Y], e, [X1,Y]) :- X1 is X+1.
i have some issues with backtracking in SWI-Prolog
In my predicate i have 2 lists as an input and the result is a third one.
I take from L1 each element with nth0/3, then i use another predicate that i need, so i append to the third list the second and the element if other_pred is true.
I'm using fail to force backtracking with nth0, but obviously the mypred fails every time and i can't get the final list i want.
I have also tried to use nth0 with and index I, increasing it, but it also makes fail the predicate. Same problem if i check that I is lower than L1 length for each iteration.
mypred(L1, L2, Result) :-
nth0(_, L1, X),
other_pred(X, L2),
append(L2, [X], Result), fail.
Since you did not give code for other_pred/2 this will use member/2.
mypred([H1|T1], L2, [H1|R]) :-
member(H1,L2),
mypred(T1,L2,R).
mypred([H1|T1], L2, R) :-
\+ member(H1,L2),
mypred(T1,L2,R).
mypred([],_,[]).
Example runs:
?- mypred([1,3,5], [1,2,4,5], R).
R = [1, 5] ;
false.
?- mypred([], [1,2,4,5], R).
R = [].
?- mypred([1,3,5], [], R).
R = [].
?- mypred([1,3,5], [2,4,6], R).
R = [].
?- mypred([1,3,5], [1,3,5], R).
R = [1, 3, 5] ;
false.
While you can use nth0/3 using the list constructor |/2 is much better, see: Lists
In this code [H1|T1] and [H1|R] use the list constructor.
This code also uses recursion.
The recursive clauses are
mypred([H1|T1], L2, [H1|R]) :-
member(H1,L2),
mypred(T1,L2,R).
mypred([H1|T1], L2, R) :-
\+ member(H1,L2),
mypred(T1,L2,R).
because the predicate mypred/3 is called in the clause. Also because the call to mypred/3 is the last call in the clause this is tail-recursive.
The base case for the recursive predicate is
mypred([],_,[]).
How this works for
mypred([1,3,5], [1,2,5], R).
is that the list [1,3,5] for the first parameter is matched with the first predicate
mypred([H1|T1], L2, [H1|R]) :-
member(H1,L2),
mypred(T1,L2,R).
This succeeds with the following unification
H1 = 1
T1 = [3,5]
L2 = [1,2,5]
R = _
The next line in the clause is executed:
member(H1,L2)
This succeeds.
The last line in the clause is executed:
mypred(T1,L2,R)
This matches the first predicate
mypred([H1|T1], L2, [H1|R]) :-
member(H1,L2),
mypred(T1,L2,R).
This succeeds with the following unification
H1 = 3
T1 = [5]
L2 = [1,2,5]
R = _
The next line in the clause is executed:
member(H1,L2)
This fails and backtracks.
Since there is another clause for my_pred/3 it is tried.
mypred([H1|T1], L2, R) :-
\+ member(H1,L2),
mypred(T1,L2,R).
This succeeds with the following unification
H1 = 3
T1 = [5]
L2 = [1,2,5]
R = _
The next line in the clause is executed:
\+ member(H1,L2)
This succeeds.
This pattern of trying different clauses for the predicate continues. At this point this will skip the details until the use of the third clause is used.
When the list for the first parameters is [], the third clause is used,
mypred([],_,[]).
Now the backtracking can begin.
Since the only lines that can call the third clause are like
mypred(T1,L2,R).
in the first and second clauses, R is unified with [].
Now depending upon which of the clauses made that call the list in the third parameter will be constructed differently.
If the second clause was used the third parameter will be constructed using
mypred([H1|T1], L2, R)
So the list is just returned unchanged.
However if the first clause was used the third parameter will be constructed using
mypred([H1|T1], L2, [H1|R])
but this time the result of the third parameter will be the value H1 when the clause was executed combined with the value of R. So if H1 is 5 and R is [] then [H1|R] is [5|[]] which is [5].
Here is a trace run for
mypred([1,3,5], [1,2,5], R).
so that you call look at all of the details.
?- trace.
[trace] ?- mypred([1,3,5], [1,2,5], R).
Call: (8) mypred([1, 3, 5], [1, 2, 5], _1844)
Unify: (8) mypred([1, 3, 5], [1, 2, 5], [1|_2090])
Call: (9) lists:member(1, [1, 2, 5])
Unify: (9) lists:member(1, [1, 2, 5])
Exit: (9) lists:member(1, [1, 2, 5])
Call: (9) mypred([3, 5], [1, 2, 5], _2090)
Unify: (9) mypred([3, 5], [1, 2, 5], [3|_2096])
Call: (10) lists:member(3, [1, 2, 5])
Unify: (10) lists:member(3, [1, 2, 5])
Fail: (10) lists:member(3, [1, 2, 5])
Redo: (9) mypred([3, 5], [1, 2, 5], _2090)
Unify: (9) mypred([3, 5], [1, 2, 5], _2090)
Call: (10) lists:member(3, [1, 2, 5])
Unify: (10) lists:member(3, [1, 2, 5])
Fail: (10) lists:member(3, [1, 2, 5])
Redo: (9) mypred([3, 5], [1, 2, 5], _2090)
Call: (10) mypred([5], [1, 2, 5], _2090)
Unify: (10) mypred([5], [1, 2, 5], [5|_2096])
Call: (11) lists:member(5, [1, 2, 5])
Unify: (11) lists:member(5, [1, 2, 5])
Exit: (11) lists:member(5, [1, 2, 5])
Call: (11) mypred([], [1, 2, 5], _2096)
Unify: (11) mypred([], [1, 2, 5], [])
Exit: (11) mypred([], [1, 2, 5], [])
Exit: (10) mypred([5], [1, 2, 5], [5])
Exit: (9) mypred([3, 5], [1, 2, 5], [5])
Exit: (8) mypred([1, 3, 5], [1, 2, 5], [1, 5])
R = [1, 5] ;
Redo: (10) mypred([5], [1, 2, 5], _2090)
Unify: (10) mypred([5], [1, 2, 5], _2090)
Call: (11) lists:member(5, [1, 2, 5])
Unify: (11) lists:member(5, [1, 2, 5])
Exit: (11) lists:member(5, [1, 2, 5])
Fail: (10) mypred([5], [1, 2, 5], _2090)
Fail: (9) mypred([3, 5], [1, 2, 5], _2090)
Redo: (9) lists:member(1, [1, 2, 5])
Fail: (9) lists:member(1, [1, 2, 5])
Redo: (8) mypred([1, 3, 5], [1, 2, 5], _1844)
Unify: (8) mypred([1, 3, 5], [1, 2, 5], _1844)
Call: (9) lists:member(1, [1, 2, 5])
Unify: (9) lists:member(1, [1, 2, 5])
Exit: (9) lists:member(1, [1, 2, 5])
Fail: (8) mypred([1, 3, 5], [1, 2, 5], _1844)
false.
If you are using SWI-Prolog then do this combination of queries to bring up the GUI tracer which is nicer for learning.
?- gtrace.
[trace] ?- mypred([1,3,5], [1,2,5], R).
Per suggestion in comment
Here are some other slight code variations and performance measures.
mypred_01([H1|T1], L2, [H1|R]) :-
member(H1,L2),
mypred_01(T1,L2,R).
mypred_01([H1|T1], L2, R) :-
\+ member(H1,L2),
mypred_01(T1,L2,R).
mypred_01([],_,[]).
mypred_02(L1,L2,R) :-
mypred_02_helper(L1,L2,[],R).
mypred_02_helper([H1|T1],L2,R0,R) :-
(
member(H1,L2)
->
mypred_02_helper(T1,L2,[H1|R0],R)
;
mypred_02_helper(T1,L2,R0,R)
).
mypred_02_helper([],_,R,R).
mypred_03(L1,L2,R) :-
mypred_03_helper(L1,L2,[],R0),
reverse(R0,R).
mypred_03_helper([H1|T1],L2,R0,R) :-
(
member(H1,L2)
->
mypred_03_helper(T1,L2,[H1|R0],R)
;
mypred_03_helper(T1,L2,R0,R)
).
mypred_03_helper([],_,R,R).
mypred_04(L1,L2,R) :-
mypred_04_helper(L1,L2,[],R).
mypred_04_helper([H1|T1],L2,R0,R) :-
(
memberchk(H1,L2)
->
mypred_04_helper(T1,L2,[H1|R0],R)
;
mypred_04_helper(T1,L2,R0,R)
).
mypred_04_helper([],_,R,R).
mypred_05(L1,L2,R) :-
mypred_05_helper(L1,L2,[],R0),
reverse(R0,R).
mypred_05_helper([H1|T1],L2,R0,R) :-
(
memberchk(H1,L2)
->
mypred_05_helper(T1,L2,[H1|R0],R)
;
mypred_05_helper(T1,L2,R0,R)
).
mypred_05_helper([],_,R,R).
Here are the performance results.
?- findall(N, between(1,100000,N), L1),time(mypred_01(L1,[1,10,100,10000,100000],R)).
% 1,400,020 inferences, 0.109 CPU in 0.103 seconds (106% CPU, 12800183 Lips)
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
R = [1, 10, 100, 10000, 100000] ;
% 36 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
false.
?- findall(N, between(1,100000,N), L1),time(mypred_02(L1,[1,10,100,10000,100000],R)).
% 799,988 inferences, 0.063 CPU in 0.062 seconds (101% CPU, 12799808 Lips)
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
R = [100000, 10000, 100, 10, 1].
?- findall(N, between(1,100000,N), L1),time(mypred_03(L1,[1,10,100,10000,100000],R)).
% 800,059 inferences, 0.047 CPU in 0.053 seconds (88% CPU, 17067925 Lips)
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
R = [1, 10, 100, 10000, 100000].
?- findall(N, between(1,100000,N), L1),time(mypred_04(L1,[1,10,100,10000,100000],R)).
% 299,999 inferences, 0.031 CPU in 0.041 seconds (77% CPU, 9599968 Lips)
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
R = [100000, 10000, 100, 10, 1].
?- findall(N, between(1,100000,N), L1),time(mypred_05(L1,[1,10,100,10000,100000],R)).
% 300,005 inferences, 0.031 CPU in 0.032 seconds (98% CPU, 9600160 Lips)
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
R = [1, 10, 100, 10000, 100000].
This is the question
find value A.
inverse([],[]).
inverse([H|T],D) :-
inverse(T,Z),
append(Z,[H],D).
append([],X,X).
append([X|L],M,[X|N]) :-
append(L,M,N).
This is the answer:
Plese help me to understand this!
The images of the Prolog code you posted show some unusual or very old Prolog, in particular for list the use of [H:T] is now done as [H|T], notice the change from : to |, and <= is more common as :-.
To understand the Prolog code it is easier to start from the bottom up. I will not cover unification or backward chaining in this as going to that level of detail would require a chapters worth here.
The first predicate to understand is append/3. Normally you never see the code for append given as it is a built-in predicate, but here it is given.
Append/3 has three parameters which are all list. The first two are appended together to form the third.
?- append_01([],[],R).
R = [].
?- append_01([a],[],R).
R = [a].
?- append_01([],[a],R).
R = [a].
?- append_01([a],[b],R).
R = [a, b].
but Prolog predicates can have other modes of operation which can bind values to what would be considered input parameters in other programming languages, e.g.
?- append(X,[b],[a,b]).
X = [a] ;
false.
?- append_01([a],Y,[a,b]).
Y = [b].
?- append(X,Y,[a,b]).
X = [] , Y = [a, b] ;
X = [a] , Y = [b] ;
X = [a, b], Y = [] ;
false.
or can just be used to verify the arguments
?- append([a],[b],[a,b]).
true.
?- append([a],[c],[a,b]).
false.
Next is the predicate inverse/2 which is more commonly known in Prolog as reverse/2, and again the source code is given here.
This simply takes one list and reverses it, e.g.
?- inverse([],X).
X = [].
?- inverse([a],X).
X = [a].
?- inverse([a,b],X).
X = [b, a].
however this version of the source code does not do well in other modes, e.g.
?- inverse(X,[]).
X = [] ;
Action (h for help) ? abort
% Execution Aborted
but that doesn't matter to answer the question.
The next part of what you posted is a trace of the execution of the query
?- inverse([[1,2,3],[5,4]],A).
In order to use the trace on your code, since there is a built-in predicate for append/3 I had to rename the predicate. Here is the code I used.
inverse([],[]).
inverse([H|T],D) :-
inverse(T,Z),
append_01(Z,[H],D).
append_01([],X,X).
append_01([X|L],M,[X|N]) :-
append_01(L,M,N).
Using SWI-Prolog
set up the trace
?- visible(+all),leash(-all).
start the trace
trace.
execute the query
[trace] ?- inverse([[1,2,3],[5,4]],A).
returns
Call: (8) inverse([[1, 2, 3], [5, 4]], _7548)
Unify: (8) inverse([[1, 2, 3], [5, 4]], _7548)
Call: (9) inverse([[5, 4]], _7794)
Unify: (9) inverse([[5, 4]], _7794)
Call: (10) inverse([], _7794)
Unify: (10) inverse([], [])
Exit: (10) inverse([], [])
Call: (10) append_01([], [[5, 4]], _7802)
Unify: (10) append_01([], [[5, 4]], [[5, 4]])
Exit: (10) append_01([], [[5, 4]], [[5, 4]])
Exit: (9) inverse([[5, 4]], [[5, 4]])
Call: (9) append_01([[5, 4]], [[1, 2, 3]], _7548)
Unify: (9) append_01([[5, 4]], [[1, 2, 3]], [[5, 4]|_7792])
Call: (10) append_01([], [[1, 2, 3]], _7792)
Unify: (10) append_01([], [[1, 2, 3]], [[1, 2, 3]])
Exit: (10) append_01([], [[1, 2, 3]], [[1, 2, 3]])
Exit: (9) append_01([[5, 4]], [[1, 2, 3]], [[5, 4], [1, 2, 3]])
Exit: (8) inverse([[1, 2, 3], [5, 4]], [[5, 4], [1, 2, 3]])
A = [[5, 4], [1, 2, 3]].
I will not explain the details of a trace as other SO Q&A do that.
The trace you posted also has more detail than generated by using the trace, e.g. the bindings (θ).
To see the bindings use gtrace/0
?- gtrace.
% The graphical front-end will be used for subsequent tracing
true.
then execute the query
[trace]?- inverse([[1,2,3],[5,4]],A).
and press space bar to single step. You will have to experiment with it to learn how it works; AFAIK there is no posted documentation on how to use it.
From OP comment:
There are some replacements from letters to numbers and theta symbol that make me hard to understand.
While the bindings (θ) are more specific to logic languages, the numbers are also seen in stack based functional languages, see De Bruijn index. Also instead of writing the bindings using vertical line separator (---) , I prefer to use (↦) as seen here.
The lines 1 -4 are just the source code stated again.
Normally with a trace the goal is to covey a tree structure of the executions (calls) but with these lines unless you know how Prolog works it is really hard to see that there is a tree structure.
The lines with the over-bars are meant to help understand what is going on, but if you just follow the flow of the executions (calls) then you may find as I do that they just cause confusion and can be ignored.
At you noted in the comments the Res(_,_) are referring to previous lines in the trace. So Res(5,2) on line 6 can be read as Line 6 is the result of a call from line 5 and which then calls line 2.
The unifications or bindings (θ) are show as as sets. I am not exactly sure what the super and sub script numbers represent but they are clearly linked to De Bruijn indexes. You will have to ask your teacher to explain the super and sub scripts.
After trying several times to explain this with just text, I finally resorted to using Microsoft Visio to do it as graphical tree which was much easier, faster and more accurate.
Even though it was not needed, I added the line trace output from SWI-Prolog into the image and then placed only the call lines in the corresponding places in the tree so that if you want to correlate the two you can. I did it for myself as a check to make sure it was correct.
I would not be surprised if there are a few typo mistakes as I had to redo parts of it many times to make it easy to comprehend. Hopefully I achieved that goal.
I have this insertion sort to sort a list in descending order in Prolog and it works:
insert(X,[],[X]).
insert(X, [Y|Tail], [X,Y|Tail]):- X > Y, !.
insert(X, [Y|Tail], [Y|NTail]):- insert(X, Tail, NTail).
ins_sort([], []).
ins_sort([X|Tail], Sorted):- ins_sort(Tail, STail), insert(X, STail, Sorted).
I am running it on SWISH and trying to understand how it functions with the following trace:
Call:ins_sort([1, 2, 3, 4, 5], _12162)
Call:ins_sort([2, 3, 4, 5], _12358)
Call:ins_sort([3, 4, 5], _12358)
Call:ins_sort([4, 5], _12358)
Call:ins_sort([5], _12358)
Call:ins_sort([], _12358)
Exit:ins_sort([], [])
Call:insert(5, [], _12360)
Exit:insert(5, [], [5])
Exit:ins_sort([5], [5])
Call:insert(4, [5], _12366)
Call:4>5
Fail:4>5
Redo:insert(4, [5], _12370)
Call:insert(4, [], _12282)
Exit:insert(4, [], [4])
Exit:insert(4, [5], [5, 4])
Exit:ins_sort([4, 5], [5, 4])
Call:insert(3, [5, 4], _12378)
Call:3>5
Fail:3>5
Redo:insert(3, [5, 4], _12382)
Call:insert(3, [4], _12294)
Call:3>4
Fail:3>4
Redo:insert(3, [4], _12294)
Call:insert(3, [], _12300)
Exit:insert(3, [], [3])
Exit:insert(3, [4], [4, 3])
Exit:insert(3, [5, 4], [5, 4, 3])
Exit:ins_sort([3, 4, 5], [5, 4, 3])
Call:insert(2, [5, 4, 3], _12396)
Call:2>5
Fail:2>5
Redo:insert(2, [5, 4, 3], _12400)
Call:insert(2, [4, 3], _12312)
Call:2>4
Fail:2>4
Redo:insert(2, [4, 3], _12312)
Call:insert(2, [3], _12318)
Call:2>3
Fail:2>3
Redo:insert(2, [3], _12318)
Call:insert(2, [], _12324)
Exit:insert(2, [], [2])
Exit:insert(2, [3], [3, 2])
Exit:insert(2, [4, 3], [4, 3, 2])
Exit:insert(2, [5, 4, 3], [5, 4, 3, 2])
Exit:ins_sort([2, 3, 4, 5], [5, 4, 3, 2])
Call:insert(1, [5, 4, 3, 2], _12162)
Call:1>5
Fail:1>5
Redo:insert(1, [5, 4, 3, 2], _12162)
Call:insert(1, [4, 3, 2], _12336)
Call:1>4
Fail:1>4
Redo:insert(1, [4, 3, 2], _12336)
Call:insert(1, [3, 2], _12342)
Call:1>3
Fail:1>3
Redo:insert(1, [3, 2], _12342)
Call:insert(1, [2], _12348)
Call:1>2
Fail:1>2
Redo:insert(1, [2], _12348)
Call:insert(1, [], _12354)
Exit:insert(1, [], [1])
Exit:insert(1, [2], [2, 1])
Exit:insert(1, [3, 2], [3, 2, 1])
Exit:insert(1, [4, 3, 2], [4, 3, 2, 1])
Exit:insert(1, [5, 4, 3, 2], [5, 4, 3, 2, 1])
Exit:ins_sort([1, 2, 3, 4, 5], [5, 4, 3, 2, 1])
I get lost once I get beyond the first "Exit". I understand all of the recursive calls until we get to an empty list, which stops the recursive calls because of the other fact and begins going back, but why after the first exit on line 7 does the undefined STail become an empty list [] in the insert call?
Has the exit of ins_sort([], []) set STail to an empty set [] and does this mean that the last argument of a fact is a return value or something?
Traces are much too hard. Re-writing is usually much easier, especially with the deterministic predicates like you have here. Long variable names are also too distracting. Instead of reading and remembering, it just might be easier simply seeing:
insert(X, [], [X]). %1
insert(X, [Y|T], [X,Y|T] ):- X > Y, !. % X was indeed greater than Y: %2
% accept the solution and stop; %3
insert(X, [Y|T], [ Y|NT]):- insert(X, T, NT). % otherwise, insert into tail. %4
%5
ins_sort( [], []). % rule {1} %6
ins_sort( [X|T], S):- % rule {2} %7
ins_sort( T, ST), %8
insert( X, ST, %9
S). %10
Let's try it with a shorter list,
ins_sort([1, 2, 3], S) ? S. %11
= \ / %12
{2: [X1|T1]=[1,2,3] } / %13
ins_sort(T1, ST1), insert(X1, ST1, S). %14
= \ / %15
{2: [X2|T2]=T1=[2,3] } / %16
ins_sort(T2, ST2), insert(X2, ST2, ST1). %17
= \ / %18
{2: [X3|T3]=T2=[3] } / %19
ins_sort(T3, ST3), insert(X3, ST3, ST2). %20
= \ / %21
{1: T3=[] ST3=[] }. %22
and we go by the V-shaped trace from the top left corner down to the middle, winding up the recursion until we reach the base case, and then up and to the right while unwinding the recursion and building the result on our way back from the base case, as usual. Thus we proceed to establish, from the bottom up,
ST3 = []. %22
insert( {X3=3}, {ST3=[]}, ST2 ):- ST2 = [3]. %20
insert( {X2=2}, {ST2=[3]}, ST1 ):- ST1 = [3,2]. %17
insert( {X1=1}, {ST1=[3,2]}, S ):- S = [3,2,1]. %14
And that's that.
I think the problem here is you are having some difficulty understanding what happens with variables during recursion. Let's take a simplified case:
count([], 0).
count([X|Xs], N) :- count(Xs, N0), succ(N0, N).
What happens when I call count([a,b], N) is this:
count([a, b], N)
+-> count([b], N)
The first thing we have to do upon entering count([a,b], N) is a recursive call to count/2. When Prolog re-enters count, we suddenly have a new set of bindings for X and Xs. In the outer call, X=a and Xs=[b], but in the inner call, X=b and Xs=[]. There will then be a third inner call, which begins with the Xs value []. This corresponds to the first three lines of this trace:
Call: (8) count([a, b], _8636) ? creep
Call: (9) count([b], _8866) ? creep
Call: (10) count([], _8866) ? creep
What the tracer is telling you here is "I am trying to enter this predicate with these values and variables." Note that the variable actually changed for N between the first and second calls.
Now, you'll notice that the [] cannot match the second clause, only the first. The first doesn't have a body but it does establish a binding. So the next line of the trace will reflect that:
Exit: (10) count([], 0) ? creep
See the numbers on the side? That's telling you the depth of the call stack. It's convenient to use numbers for traces instead of showing the nesting visually because eventually our call stacks are going to get pretty deep!
Now that we have a value for the variable, it's going to move on to the next expression in the clause we're in:
Call: (10) succ(0, _8866) ? creep
Exit: (10) succ(0, 1) ? creep
Nesting level went up one but was immediately resolved; this is par for the course with built-ins like succ/2. Now let's look at the rest of the trace:
Exit: (9) count([b], 1) ? creep
Call: (9) succ(1, _8636) ? creep
Exit: (9) succ(1, 2) ? creep
Exit: (8) count([a, b], 2) ? creep
So now that we had a binding for the recursive call, we can enter the next step in the parent call, and so on, until the whole call is resolved and we get 2.
Let's see it again, this time with nesting instead of numbers:
[trace] ?- count([a,b],N).
Call: (8) count([a, b], _8636) ? creep
Call: (9) count([b], _8866) ? creep
Call: (10) count([], _8866) ? creep
Exit: (10) count([], 0) ? creep
Call: (10) succ(0, _8866) ? creep
Exit: (10) succ(0, 1) ? creep
Exit: (9) count([b], 1) ? creep
Call: (9) succ(1, _8636) ? creep
Exit: (9) succ(1, 2) ? creep
Exit: (8) count([a, b], 2) ? creep
N = 2.
This should make what's going on in your own trace a little easier to understand.
Prolog works with unification and pattern matching.You are removing the Head and calling the predicate again with tail which keeps removing Head and tries to find matches after each call and at some later time you have an empty list so prolog search your file for a match and at this point it finds a match ins_sort([], []).
At 6th call you have Call:ins_sort([], _12358) where _12358 is a variable and this variable will get the value from ns_sort([], []). which is a fact. It simply means that if you have one empty list in ns_sort and one variable then set that variable also equals to the empty list, variable gets instantiated with anything if you have all other "terms" matching.
Prolog can be easily understood by learning Backtracking and backtracking is an algorithm so prolog self is an algorithm.