Prolog list recursion - prolog

The goal of my predicate is:
?- line_terminal_stations(east_london, StartsAt, EndsAt).
StartsAt = shoreditch
EndsAt = new_cross
Below is what I have so far, the recursion works as expected and progressively creates a list of stations on the line.
line_terminal_stations(LineName, StationX, StationY):-
next_station(LineName, StationX, StationY, []).
next_station(LineName, StationX, StationY, V) :-
link(StationX, StationY, LineName),
next_station(LineName, StationY, _, [StationX | V]).
However once the final station has been found the predicate fails and begins to 'undo' the list. Whereas when link/3 fails, i want to end the recursion so i can extract the first and last station of the list.
Examples of link/3:
link(shoreditch, whitechapel, east_london).
link(whitechapel, shadwell, east_london).
Example of run-through:
line_terminal_stations(east_london, StartsAt, EndsAt).
Redo: (9) link(_G3031, _G3032, east_london) ? creep
Exit: (9) link(whitechapel, shadwell, east_london) ? creep
Call: (9) next_station(east_london, shadwell, _G3128, [whitechapel]) ? creep
Call: (10) link(shadwell, _G3127, east_london) ? creep
Exit: (10) link(shadwell, wapping, east_london) ? creep
Call: (10) next_station(east_london, wapping, _G3131, [shadwell, whitechapel]) ? creep
Call: (11) link(wapping, _G3130, east_london) ? creep
Exit: (11) link(wapping, rotherhithe, east_london) ? creep
Call: (11) next_station(east_london, rotherhithe, _G3134, [wapping, shadwell, whitechapel]) ? creep
Call: (12) link(rotherhithe, _G3133, east_london) ? creep
Exit: (12) link(rotherhithe, surrey_docks, east_london) ? creep
Call: (12) next_station(east_london, surrey_docks, _G3137, [rotherhithe, wapping, shadwell, whitechapel]) ? creep
Call: (13) link(surrey_docks, _G3136, east_london) ? creep
Exit: (13) link(surrey_docks, new_cross_gate, east_london) ? creep
Call: (13) next_station(east_london, new_cross_gate, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Call: (14) link(new_cross_gate, _G3139, east_london) ? creep
Fail: (14) link(new_cross_gate, _G3139, east_london) ? creep
Fail: (13) next_station(east_london, new_cross_gate, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Redo: (13) link(surrey_docks, _G3136, east_london) ? creep
Exit: (13) link(surrey_docks, new_cross, east_london) ? creep
Call: (13) next_station(east_london, new_cross, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Call: (14) link(new_cross, _G3139, east_london) ? creep
Fail: (14) link(new_cross, _G3139, east_london) ? creep
Fail: (13) next_station(east_london, new_cross, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Fail: (12) next_station(east_london, surrey_docks, _G3137, [rotherhithe, wapping, shadwell, whitechapel]) ? creep
Fail: (11) next_station(east_london, rotherhithe, _G3134, [wapping, shadwell, whitechapel]) ? creep
Fail: (10) next_station(east_london, wapping, _G3131, [shadwell, whitechapel]) ? creep
Fail: (9) next_station(east_london, shadwell, _G3128, [whitechapel]) ? creep

One approach with minimum code changes would be:
link(shoreditch, whitechapel, east_london).
link(whitechapel, shadwell, east_london).
line_terminal_stations(LineName, First, Last):-
next_station(LineName, _, _, [], Stations),
Stations = [Last|_],
last(Stations, First).
next_station(LineName, StationX, StationY, V, [StationX|V]) :-
\+ link(StationX, StationY, LineName).
next_station(LineName, StationX, StationY, V, Stations) :-
link(StationX, StationY, LineName),
next_station(LineName, StationY, _, [StationX | V], Stations).
Test run:
[debug] ?- line_terminal_stations(east_london, StartsAt, EndsAt).
StartsAt = shoreditch,
EndsAt = shadwell
But as it stands the link/3 requires to be in the right order to first find the true startstation. I.e you can backtrack and find different start-station:
[debug] ?- line_terminal_stations(east_london, StartsAt, EndsAt).
StartsAt = shoreditch,
EndsAt = shadwell ;
StartsAt = whitechapel,
EndsAt = shadwell

But you need recursion?
If the line isn't a ring, you can simply impose that StartAt is a starting point, but not an ending point, and that EndAt is a ending point, but not a starting point.
I mean
line_terminal_stations(Line, StartsAt, EndsAt) :-
link(StartsAt, _, Line),
\+ link(_, StartsAt, Line),
link(_, EndsAt, Line),
\+ link(EndsAt, _, Line).

Related

Generate lists with independent variables in Prolog

I am having a problem while creating a list which I will the use to fill. I am trying to create it with independent variables, so that I can then unify with a maplist with some other lists.
For this, I did:
length(Subs, Len),
maplist(=([_, _, _]), Subs),
however, this results in this:
Subs = [[_22998, _23020, _23042], [_22998, _23020, _23042], [_22998, _23020, _23042]].
And so, when I try to unify the next way:
maplist(nth1(1), Subs, Com1),
maplist(nth1(2), Subs, Perm_sub),
maplist(nth1(3), Subs, Com3).
with:
Perm_sub = [5, 7, 6],
Com1 = [P23, P34, P36],
Com3 = [1, 3, 2].
It fails, because 5 unifies with _22998, and thus collides with 7. Is there a simple, elegant way of doing what I intend, or do I have to create a new function to handle this, and if so, how do I do the function?
You can create an auxiliary predicate as following:
triple([_, _, _]).
And ask:
?- length(L, 3), maplist(triple, L).
L = [[_2404, _2410, _2416], [_2422, _2428, _2434], [_2440, _2446, _2452]].
Alternatively, you can use a lambda expression as following:
?- length(L, 3), maplist([[_,_,_]]>>true, L).
L = [[_7198, _7204, _7210], [_7276, _7282, _7288], [_7354, _7360, _7366]].
EDIT 10/05/2021
Notice that variables used in a term are shared with their environment (they are "global", not lambda bound) and, consequently, they persist through all calls of maplist. For example, in the following query, variable X is instantiated with 1 and, after that, its value will persist through the next calls.
?- trace, maplist(=(X), [1,Y,1]).
^ Call: (11) apply:maplist(=(_1882), [1, _1892, 1]) ? creep
Call: (12) apply:maplist_([1, _1892, 1], user: =(_1882)) ? creep
Call: (13) _1882=1 ? creep
Exit: (13) 1=1 ? creep
Call: (13) apply:maplist_([_1892, 1], user: =(1)) ? creep
Call: (14) 1=_1892 ? creep
Exit: (14) 1=1 ? creep
Call: (14) apply:maplist_([1], user: =(1)) ? creep
Call: (15) 1=1 ? creep
Exit: (15) 1=1 ? creep
Call: (15) apply:maplist_([], user: =(1)) ? creep
Exit: (15) apply:maplist_([], user: =(1)) ? creep
Exit: (14) apply:maplist_([1], user: =(1)) ? creep
Exit: (13) apply:maplist_([1, 1], user: =(1)) ? creep
Exit: (12) apply:maplist_([1, 1, 1], user: =(1)) ? creep
^ Exit: (11) apply:maplist(user: =(1), [1, 1, 1]) ? creep
X = Y, Y = 1.
On the other hand, variables used as predicate arguments are "local" and their instantiations do not persist from one call to another. For example:
?- [user].
| equal(_X).
| ^Z
% user://1 compiled 0.02 sec, 1 clauses
true.
?- trace, maplist(equal, [1,Y,1]).
^ Call: (11) apply:maplist(equal, [1, _1752, 1]) ? creep
Call: (12) apply:maplist_([1, _1752, 1], user:equal) ? creep
Call: (13) equal(1) ? creep
Exit: (13) equal(1) ? creep
Call: (13) apply:maplist_([_1752, 1], user:equal) ? creep
Call: (14) equal(_1752) ? creep
Exit: (14) equal(_1752) ? creep
Call: (14) apply:maplist_([1], user:equal) ? creep
Call: (15) equal(1) ? creep
Exit: (15) equal(1) ? creep
Call: (15) apply:maplist_([], user:equal) ? creep
Exit: (15) apply:maplist_([], user:equal) ? creep
Exit: (14) apply:maplist_([1], user:equal) ? creep
Exit: (13) apply:maplist_([_1752, 1], user:equal) ? creep
Exit: (12) apply:maplist_([1, _1752, 1], user:equal) ? creep
^ Exit: (11) apply:maplist(user:equal, [1, _1752, 1]) ? creep
true.

Prolog checking the same fact twice

This is the knowledge database
student(jack,100,21,m).
takes(100,cs01).
takes(100,cs02).
takes(100,cs03).
teaches(doe,cs01).
course(cs01,ai).
course(cs02,cpp).
course(cs03,java).
isTaughtBy(Sname,Lname) :-
teaches(Lname,Mcode),
student(Sname,Scode,_,_),
takes(Scode,Mcode).
When running isTaughtBy(jack,doe) it returns true (like its supposed to) and false (for some reason, probably because jack takes multiple modules).
This is the trace:
[trace] ?- isTaughtBy(jack,doe).
Call: (8) isTaughtBy(jack, doe) ? creep
Call: (9) teaches(doe, _18526) ? creep
Exit: (9) teaches(doe, cs01) ? creep
Call: (9) student(jack, _18526, _18528, _18530) ? creep
Exit: (9) student(jack, 100, 21, m) ? creep
Call: (9) takes(100, cs01) ? creep
Exit: (9) takes(100, cs01) ? creep
Exit: (8) isTaughtBy(jack, doe) ? creep
true ;
Redo: (9) takes(100, cs01) ? creep
Fail: (9) takes(100, cs01) ? creep
Fail: (8) isTaughtBy(jack, doe) ? creep
false.
Why is it redoing takes(100,cs01) if it was already checked above? Why is it returning false even though its clearly defined in the database? What am i not understanding here? I just want it to return true or false if a student is taught by a lecturer on any of the modules they are taking.

When is the Redo-port called with new variables in Trace/0 and when not?

During my implementation of the Adventure game in Prolog I was wondering when the redo-port is called with new variables during backtracking and when it is called with the same ones?
For example, I have the following knowledge base:
location(desk,office).
location(apple,kitchen).
location(flashlight,desk).
location('washing machine',cellar).
location(nani,'washing machine').
location(broccoli,kitchen).
location(crackers,kitchen).
location(computer,office).
door(office,hall).
door(kitchen,office).
door(hall,'dining room').
door(kitchen,cellar).
door('dining room',kitchen).
When tracing the query ?-(location(X,Y),door(kitchen,Y)). I get this:
Call: (9) location(_7998, _8000) ? creep
Exit: (9) location(desk, office) ? creep
Call: (9) door(kitchen, office) ? creep
Exit: (9) door(kitchen, office) ? creep
X = desk,
Y = office ;
Redo: (9) door(kitchen, office) ? creep <==== 1
Fail: (9) door(kitchen, office) ? creep
Redo: (9) location(_7998, _8000) ? creep
Exit: (9) location(apple, kitchen) ? creep
Call: (9) door(kitchen, kitchen) ? creep
Fail: (9) door(kitchen, kitchen) ? creep
Redo: (9) location(_7998, _8000) ? creep
Exit: (9) location(flashlight, desk) ? creep
Call: (9) door(kitchen, desk) ? creep
Fail: (9) door(kitchen, desk) ? creep
Redo: (9) location(_7998, _8000) ? creep
Exit: (9) location('washing machine', cellar) ? creep
Call: (9) door(kitchen, cellar) ? creep
Exit: (9) door(kitchen, cellar) ? creep
X = 'washing machine',
Y = cellar ;
Redo: (9) location(_7998, _8000) ? creep <==== 2
Exit: (9) location(nani, 'washing machine') ? creep
Call: (9) door(kitchen, 'washing machine') ? creep
Fail: (9) door(kitchen, 'washing machine') ? creep
Redo: (9) location(_7998, _8000) ? creep
Exit: (9) location(broccoli, kitchen) ? creep
Call: (9) door(kitchen, kitchen) ? creep
Fail: (9) door(kitchen, kitchen) ? creep
Redo: (9) location(_7998, _8000) ? creep
Exit: (9) location(crackers, kitchen) ? creep
Call: (9) door(kitchen, kitchen) ? creep
Fail: (9) door(kitchen, kitchen) ? creep
Redo: (9) location(_7998, _8000) ? creep
Exit: (9) location(computer, office) ? creep
Call: (9) door(kitchen, office) ? creep
Exit: (9) door(kitchen, office) ? creep
X = computer,
Y = office ;
Redo: (9) door(kitchen, office) ? creep <==== 3
Fail: (9) door(kitchen, office) ? creep
false.
I understand how the binding of the variables works, but I do not understand why Prolog returns
Redo: (9) door(kitchen, office) ? creep <==== 1
Redo: (9) door(kitchen, office) ? creep <==== 3
after finding the first solution (and the same for the last solution),
while it immediately looks for a new binding of X and Y after the second solution
Redo: (9) location(_7998, _8000) ? creep <==== 2
Why does it not immediately continue to look for a new binding of X and Y after the first and last solution, instead of resorting to
Redo: (9) door(kitchen, office) ? creep <==== 3
(which fails anyway)?
(I found a similar question here, but unfortunately my reputation does not allow me to comment over there and I think this particular issue does not have to do with my Prolog version (as I tried it with SWI-Prolog and GNUProlog))
Thanks in advance for offering any clarity.
This answer makes use of SWI-Prolog.
The first thing that can help when using trace for the first time is to make all of the ports visible. By default the unify port is not visible but can be made visible with
?- visible(+unify).
Now since this is such a short running query, instead of having to press the space bar for each port, we can disable the leashing of the leashed ports with
?- leash(-call).
?- leash(-exit).
?- leash(-redo).
?- leash(-fail).
Now if you turn on trace and run the query
?- trace.
[trace] ?- (location(X,Y),door(kitchen,Y)).
you will not have to press the space bar expect for answers.
Doing so will return
Call: (9) location(_9632, _9634)
Unify: (9) location(desk, office)
Exit: (9) location(desk, office)
Call: (9) door(kitchen, office)
Unify: (9) door(kitchen, office)
Exit: (9) door(kitchen, office)
X = desk,
Y = office ;
Redo: (9) door(kitchen, office)
Fail: (9) door(kitchen, office)
Redo: (9) location(_9632, _9634)
Unify: (9) location(apple, kitchen)
Exit: (9) location(apple, kitchen)
Call: (9) door(kitchen, kitchen)
Fail: (9) door(kitchen, kitchen)
Redo: (9) location(_9632, _9634)
Unify: (9) location(flashlight, desk)
Exit: (9) location(flashlight, desk)
Call: (9) door(kitchen, desk)
Fail: (9) door(kitchen, desk)
Redo: (9) location(_9632, _9634)
Unify: (9) location('washing machine', cellar)
Exit: (9) location('washing machine', cellar)
Call: (9) door(kitchen, cellar)
Unify: (9) door(kitchen, cellar)
Exit: (9) door(kitchen, cellar)
X = 'washing machine',
Y = cellar ;
Redo: (9) location(_9632, _9634)
Unify: (9) location(nani, 'washing machine')
Exit: (9) location(nani, 'washing machine')
Call: (9) door(kitchen, 'washing machine')
Fail: (9) door(kitchen, 'washing machine')
Redo: (9) location(_9632, _9634)
Unify: (9) location(broccoli, kitchen)
Exit: (9) location(broccoli, kitchen)
Call: (9) door(kitchen, kitchen)
Fail: (9) door(kitchen, kitchen)
Redo: (9) location(_9632, _9634)
Unify: (9) location(crackers, kitchen)
Exit: (9) location(crackers, kitchen)
Call: (9) door(kitchen, kitchen)
Fail: (9) door(kitchen, kitchen)
Redo: (9) location(_9632, _9634)
Unify: (9) location(computer, office)
Exit: (9) location(computer, office)
Call: (9) door(kitchen, office)
Unify: (9) door(kitchen, office)
Exit: (9) door(kitchen, office)
X = computer,
Y = office ;
Redo: (9) door(kitchen, office)
Fail: (9) door(kitchen, office)
false.
which now has the Unify ports visible.
Since this is such a short query I will comment the significant lines of the trace and that in turn should answer your questions.
% location fact 1: location(desk, office) -------------------------
% First predicate of query - location(X,Y)
% The location facts are matched in the order of the source code
% Since the code is looking for location(X,Y)
% it matches location fact 1: location(desk, office)
% Since there are more location facts like location(X, Y),
% e.g.
% location(apple,kitchen).
% location(flashlight,desk).
% location('washing machine',cellar).
% location(nani,'washing machine').
% location(broccoli,kitchen).
% location(crackers,kitchen).
% location(computer,office).
% a choice point is generated
% choice point: location 1
Call: (9) location(_9632, _9634)
% Unifies with first location fact.
% X binds with desk
% Y binds with office
Unify: (9) location(desk, office)
Exit: (9) location(desk, office)
% Second predicate of query - door(kitchen,Y)).
% Y is bound with office
% The door facts are matched in the order of the source code
% Since the code is only looking for door(kitchen,office)
% it matches door fact 2: door(kitchen,office)
% Since there are more door facts like door(kitchen,Y),
% e.g.
% door(kitchen,cellar).
% a choice point is generated
% choice point: door 1
Call: (9) door(kitchen, office)
% Since there is a door(kitchen, office) fact
% unify with second predicate
Unify: (9) door(kitchen, office)
Exit: (9) door(kitchen, office)
% No more predicates in the query so return result.
X = desk,
Y = office ;
% Remember choice point: door 1
% Use the second predicate
% on the remaining door facts like door(kitchen,Y)
% e.g.
% door(kitchen,cellar).
Redo: (9) door(kitchen, office)
% There are no more door facts that unify with door(kitchen, office)
% so fail.
Fail: (9) door(kitchen, office)
% Since the second predicate failed,
% go back to the first predicate location(X,Y)
% location fact 2: location(apple, kitchen) -----------------------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location(apple,kitchen).
% location(flashlight,desk).
% location('washing machine',cellar).
% location(nani,'washing machine').
% location(broccoli,kitchen).
% location(crackers,kitchen).
% location(computer,office).
Redo: (9) location(_9632, _9634)
% The second fact unifies with the first predicate location(X,Y)
% X binds with apple
% Y binds with kitchen
Unify: (9) location(apple, kitchen)
Exit: (9) location(apple, kitchen)
% Second predicate of query - door(kitchen,Y)).
% Y is bound with kitchen
% The door facts are matched in the order of the source code
% Since the code is only looking for door(kitchen,kitchen)
% it matches none of the door facts
% and since it checked all of the door facts
% no choice point was generated.
Call: (9) door(kitchen, kitchen)
% There is no door(kitchen, kitchen) fact so fail.
Fail: (9) door(kitchen, kitchen)
% location fact 3: location(flashlight, desk) ---------------------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location(flashlight,desk).
% location('washing machine',cellar).
% location(nani,'washing machine').
% location(broccoli,kitchen).
% location(crackers,kitchen).
% location(computer,office).
Redo: (9) location(_9632, _9634)
Unify: (9) location(flashlight, desk)
Exit: (9) location(flashlight, desk)
Call: (9) door(kitchen, desk)
Fail: (9) door(kitchen, desk)
% Since the second predicate failed,
% go back to the first predicate location(X,Y)
% location fact 4: location('washing machine', cellar) -----------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location('washing machine',cellar).
% location(nani,'washing machine').
% location(broccoli,kitchen).
% location(crackers,kitchen).
% location(computer,office).
Redo: (9) location(_9632, _9634)
% The forth fact unifies with the first predicate location(X,Y)
% X binds with 'washing machine'
% Y binds with cellar
Unify: (9) location('washing machine', cellar)
Exit: (9) location('washing machine', cellar)
% Second predicate of query - door(kitchen,Y)).
% Y is bound with cellar
% The door facts are matched in the order of the source code
% Since the code is only looking for door(kitchen,cellar)
% it matches door fact 4: door(kitchen,cellar)
% Since there are NO more door facts like door(kitchen,Y),
% NO choice point is generated
Call: (9) door(kitchen, cellar)
% There is a door(kitchen, cellar) fact so unify.
Unify: (9) door(kitchen, cellar)
Exit: (9) door(kitchen, cellar)
% No more predicates in the query so return result.
X = 'washing machine',
Y = cellar ;
% location fact 5: location(nani, 'washing machine') --------------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location(nani,'washing machine').
% location(broccoli,kitchen).
% location(crackers,kitchen).
% location(computer,office).
Redo: (9) location(_9632, _9634)
Unify: (9) location(nani, 'washing machine')
Exit: (9) location(nani, 'washing machine')
Call: (9) door(kitchen, 'washing machine')
Fail: (9) door(kitchen, 'washing machine')
% location fact 6: location(broccoli, kitchen) --------------------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location(broccoli,kitchen).
% location(crackers,kitchen).
% location(computer,office).
Redo: (9) location(_9632, _9634)
Unify: (9) location(broccoli, kitchen)
Exit: (9) location(broccoli, kitchen)
Call: (9) door(kitchen, kitchen)
Fail: (9) door(kitchen, kitchen)
% location fact 7: location(crackers, kitchen) --------------------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location(crackers,kitchen).
% location(computer,office).
Redo: (9) location(_9632, _9634)
Unify: (9) location(crackers, kitchen)
Exit: (9) location(crackers, kitchen)
Call: (9) door(kitchen, kitchen)
Fail: (9) door(kitchen, kitchen)
% Since the second predicate failed,
% go back to the first predicate location(X,Y)
% location fact 8: location(computer, office) ---------------------
% Remember choice point: location 1
% Use the first predicate
% on the remaining location facts like location(X,Y)
% e.g.
% location(computer,office).
Redo: (9) location(_9632, _9634)
% The last fact unifies with the first predicate location(X,Y)
% X binds with computer
% Y binds with office
Unify: (9) location(computer, office)
Exit: (9) location(computer, office)
% Second predicate of query - door(kitchen,Y)).
% Y is bound with office
% The door facts are matched in the order of the source code
% Since the code is only looking for door(kitchen,office)
% it matches door fact 2: door(kitchen,office)
% Since there are more door facts like door(kitchen,Y),
% e.g.
% door(kitchen,cellar).
% a choice point is generated
% choice point: door 2
Call: (9) door(kitchen, office)
% Since there is a door(kitchen, office) fact
% unify with second predicate
Unify: (9) door(kitchen, office)
Exit: (9) door(kitchen, office)
% No more predicates in the query so return result.
X = computer,
Y = office ;
% Remember choice point: door 2
% Use the second predicate
% on the remaining door facts like door(kitchen,Y)
% e.g.
% door(kitchen,cellar).
Redo: (9) door(kitchen, office)
% There are no more door facts that unify with door(kitchen, office)
% so fail.
Fail: (9) door(kitchen, office)
% There are no more location facts so end the query.
false.
Supplement
When I created the answer it was in a text editor that can do syntax highlight for Prolog code, and to help me keep things straight I had three panes open for comparing. One pane had the location and door rules. One pane had a comment, and the third pane had a similar comment. As I moved down the code I kept updating the comments in the third pane to match similar comment in the second pane while checking against the facts in the first pane. I note this because it is probably a better way to understand the comments than reading them from this answer as posted.
The editor is Visual Studio Code and for highlighting Prolog the Prolog extension is installed.
This is just a comment posted as a answer because it has a picture.
If you use the graphical debugger you can see when choice points are created.
?- gtrace.
true.
[trace] ?- (location(X,Y),door(kitchen,Y)).
In the following image of the debugger I highlighted the choice point with a green rectangle.
To exit out of trace mode in top-level enter nodebug.
[trace] ?- nodebug.
true.
?-
Note: It is the choice points that lead to using the redo port.
Note: Another way to debug prolog is via use of failure slice. Also read questions tagged with failure slice

How to redirect trace output to a file

I am tracing a prolog program
1 ?- trace.
true.
[trace] 1 ?- solve.
Call: (7) solve ?
I also tried
tell('trace_op.txt').
the file is created but empty
Now the trace is really many lines . I want to redirect the output to a file
Can we redirect it to a file ?
On Windows using SWI-Prolog you can use protocol/1
protocol/1 will copy the output that goes to the screen to a file. So if you run a trace/0 and the output goes to the screen, a copy will be sent to a file. To simplify having to write the entire path for protocol/1 I find it easier to set the current working directory using working_directory/2 and then only set the specific file with protocol/1.
Example
For this example create a file, e.g.
trace_example.pl
and add some facts and predicates to demonstrate tracing.
parent(ann,helen).
parent(helen,henry).
parent(henry,mary).
ancestor(X,Y) :- parent(X,Y).
ancestor(X,Y) :- parent(X,Z),
ancestor(Z,Y).
Open SWI-Prolog top level and use consult/1 to load the file
consult("C:/ ... /trace_example.pl").
N.B. The directory separators are / not \. Change them if necessary.
Since the SWI-Prolog terminal on Windows will default to using color with tracing and that will add unneeded escape sequences to the output file set_prolog_flag/2 needs to be run to turn the color off.
?- set_prolog_flag(color_term,false).
true.
Verify that the terminal is not using color.
?- current_prolog_flag(color_term,X).
X = false.
A quick run to verify the predicate and facts work
?- ancestor(ann,henry).
true ;
false.
Now set the current working directory to where the output file will be created.
?- working_directory(_,"C:/Users/Eric/Documents/Prolog").
and verify that the change occurred
?- working_directory(CWD,CWD).
CWD = 'c:/users/eric/documents/prolog/'.
Since I don't want to press the space bar for each trace output I disable user interaction for all the debug ports using leash/1
?- leash(-all).
and since I want to see all of the output from all of the debug ports I enable them all with visible/1
?- visible(+all).
Enable the copying of the screen to a file
?- protocol("./trace_output.txt").
start the tracer
?- trace.
and run a query to be traced
?- ancestor(ann,henry).
Call: (8) ancestor(ann, henry) ? creep
Call: (9) parent(ann, henry) ? creep
Fail: (9) parent(ann, henry) ? creep
Redo: (8) ancestor(ann, henry) ? creep
Call: (9) parent(ann, _1124) ? creep
Exit: (9) parent(ann, helen) ? creep
Call: (9) ancestor(helen, henry) ? creep
Call: (10) parent(helen, henry) ? creep
Exit: (10) parent(helen, henry) ? creep
Exit: (9) ancestor(helen, henry) ? creep
Exit: (8) ancestor(ann, henry) ? creep
true ;
Redo: (9) ancestor(helen, henry) ? creep
Call: (10) parent(helen, _1124) ? creep
Exit: (10) parent(helen, henry) ? creep
Call: (10) ancestor(henry, henry) ? creep
Call: (11) parent(henry, henry) ? creep
Fail: (11) parent(henry, henry) ? creep
Redo: (10) ancestor(henry, henry) ? creep
Call: (11) parent(henry, _1124) ? creep
Exit: (11) parent(henry, mary) ? creep
Call: (11) ancestor(mary, henry) ? creep
Call: (12) parent(mary, henry) ? creep
Fail: (12) parent(mary, henry) ? creep
Redo: (11) ancestor(mary, henry) ? creep
Call: (12) parent(mary, _1124) ? creep
Fail: (12) parent(mary, _1124) ? creep
Fail: (11) ancestor(mary, henry) ? creep
Fail: (10) ancestor(henry, henry) ? creep
Fail: (9) ancestor(helen, henry) ? creep
Fail: (8) ancestor(ann, henry) ? creep
false.
end the tracing
?- nodebug.
and end the copying of the screen to the file
?- noprotocol.
Now open the file C:\Users\Eric\Documents\Prolog\trace_output.txt
true.
10 ?- trace.
true.
[trace] 10 ?- ancestor(ann,henry).
Call: (8) ancestor(ann, henry)
Unify: (8) ancestor(ann, henry)
Call: (9) parent(ann, henry)
Fail: (9) parent(ann, henry)
Redo: (8) ancestor(ann, henry)
Unify: (8) ancestor(ann, henry)
Call: (9) parent(ann, _6466)
Unify: (9) parent(ann, helen)
Exit: (9) parent(ann, helen)
Call: (9) ancestor(helen, henry)
Unify: (9) ancestor(helen, henry)
Call: (10) parent(helen, henry)
Unify: (10) parent(helen, henry)
Exit: (10) parent(helen, henry)
Exit: (9) ancestor(helen, henry)
Exit: (8) ancestor(ann, henry)
true ;
Redo: (9) ancestor(helen, henry)
Unify: (9) ancestor(helen, henry)
Call: (10) parent(helen, _6466)
Unify: (10) parent(helen, henry)
Exit: (10) parent(helen, henry)
Call: (10) ancestor(henry, henry)
Unify: (10) ancestor(henry, henry)
Call: (11) parent(henry, henry)
Fail: (11) parent(henry, henry)
Redo: (10) ancestor(henry, henry)
Unify: (10) ancestor(henry, henry)
Call: (11) parent(henry, _6466)
Unify: (11) parent(henry, mary)
Exit: (11) parent(henry, mary)
Call: (11) ancestor(mary, henry)
Unify: (11) ancestor(mary, henry)
Call: (12) parent(mary, henry)
Fail: (12) parent(mary, henry)
Redo: (11) ancestor(mary, henry)
Unify: (11) ancestor(mary, henry)
Call: (12) parent(mary, _6466)
Fail: (12) parent(mary, _6466)
Fail: (11) ancestor(mary, henry)
Fail: (10) ancestor(henry, henry)
Fail: (9) ancestor(helen, henry)
Fail: (8) ancestor(ann, henry)
false.
[trace] 11 ?- nodebug.
true.
12 ?- noprotocol.
Linux
If you're using Linux, you can use the tee command:
$ swipl 2>&1 | tee swipl.log
...
1 ?- trace.
true.
[trace] 1 ?- solve.
Call: (7) solve ?
...
The tee command sends all of its standard input to the standard output and to to the specified file in parallel. The 2>&1 ensures that you also capture any standard error in the standard output as well so it would appear in the swipl.log file.
Windows
In Windows, if you use PowerShell, you can use the Tee-Object command which works similarly:
swipl-win | Tee-Object -file swipl.log
I'm assuming here that swipl-win is what the command line program for SWI Prolog in Windows, and that it exists in your program path for PowerShell.
When you exit swipl, then you can see all of what happened in swipl.log.

Maximum Subarray (Kadane's algorithm) - Tail recursion

i am trying to implement Kadane's Algorithm in Prolog.
One of the requirements is a tail call (recursion).
I have tried many possibilities but without success.
Here is my code:
max_sum(L, S) :-
S is 0,
H is 0,
max_sum(L, H, S).
max_sum([], S, S).
max_sum([X | L], H, S) :-
( H + X < 0 -> NewH is 0; NewH is H + X),
( S < H + X -> NewS is NewH; NewS is S),
length(L, N),
( N < 1 -> max_sum(L, NewS, NewS); max_sum(L, NewH, NewS)).
NewH, NewS are temp values (we cant assign a value twice in Prolog right?).
Can i ask for a hint?
Edit:
[trace] ?- max_sum([1, 2, 3], S).
Call: (7) max_sum([1, 2, 3], _G8907) ? creep
Call: (8) _G8907 is 0 ? creep
Exit: (8) 0 is 0 ? creep
Call: (8) _G8991 is 0 ? creep
Exit: (8) 0 is 0 ? creep
Call: (8) max_sum([1, 2, 3], 0, 0) ? creep
Call: (9) 0+1<0 ? creep
Fail: (9) 0+1<0 ? creep
Redo: (8) max_sum([1, 2, 3], 0, 0) ? creep
Call: (9) _G8994 is 0+1 ? creep
Exit: (9) 1 is 0+1 ? creep
Call: (9) 0<0+1 ? creep
Exit: (9) 0<0+1 ? creep
Call: (9) _G8997 is 1 ? creep
Exit: (9) 1 is 1 ? creep
Call: (9) length([2, 3], _G8998) ? creep
Exit: (9) length([2, 3], 2) ? creep
Call: (9) 2<1 ? creep
Fail: (9) 2<1 ? creep
Redo: (8) max_sum([1, 2, 3], 0, 0) ? creep
Call: (9) max_sum([2, 3], 1, 1) ? creep
Call: (10) 1+2<0 ? creep
Fail: (10) 1+2<0 ? creep
Redo: (9) max_sum([2, 3], 1, 1) ? creep
Call: (10) _G9000 is 1+2 ? creep
Exit: (10) 3 is 1+2 ? creep
Call: (10) 1<1+2 ? creep
Exit: (10) 1<1+2 ? creep
Call: (10) _G9003 is 3 ? creep
Exit: (10) 3 is 3 ? creep
Call: (10) length([3], _G9004) ? creep
Exit: (10) length([3], 1) ? creep
Call: (10) 1<1 ? creep
Fail: (10) 1<1 ? creep
Redo: (9) max_sum([2, 3], 1, 1) ? creep
Call: (10) max_sum([3], 3, 3) ? creep
Call: (11) 3+3<0 ? creep
Fail: (11) 3+3<0 ? creep
Redo: (10) max_sum([3], 3, 3) ? creep
Call: (11) _G9006 is 3+3 ? creep
Exit: (11) 6 is 3+3 ? creep
Call: (11) 3<3+3 ? creep
Exit: (11) 3<3+3 ? creep
Call: (11) _G9009 is 6 ? creep
Exit: (11) 6 is 6 ? creep
Call: (11) length([], _G9010) ? creep
Exit: (11) length([], 0) ? creep
Call: (11) 0<1 ? creep
Exit: (11) 0<1 ? creep
Call: (11) max_sum([], 6, 6) ? creep
Exit: (11) max_sum([], 6, 6) ? creep
Exit: (10) max_sum([3], 3, 3) ? creep
Exit: (9) max_sum([2, 3], 1, 1) ? creep
Exit: (8) max_sum([1, 2, 3], 0, 0) ? creep
Exit: (7) max_sum([1, 2, 3], 0) ? creep
In Call(11) i have a good result (6) from this simple example. How can I end the function at this point without returning? It is my problem.
Result from this code is S = 0, not S = 6.
Final edit (working code):
max_sum(L, S) :-
max_sum(L, 0, 0, S).
max_sum([], _, S, S).
max_sum([X | L], H, F, S) :-
NewH is max(0, H + X),
(F < H + X -> NewF is NewH; NewF is F),
max_sum(L, NewH, NewF, S).
Where:
S - final result,
F - maximum_so_far,
H - maximum_ending_here,
X - head of list,
L - list,
NewH, NewF - temp values.
Thanks for the help :)
This question is, in fact, a duplicate of
"Finding the maximum sublist in Prolog".
There is a bounty is offered for it, so it cannot be flagged as a duplicate.
I propose using my previous solution—it is based on clpfd and runs with SWI-Prolog.
I propose a slightly altered version of the solution proposed by #repeat:
:- use_module(library(clpfd)).
zs_max([Z|Zs], MSF) :-
zs_max_(Zs, Z, Z, MSF).
zs_max_([], _, MSF, MSF).
zs_max_([Z|Zs], MEH0, MSF0, MSF) :-
max(Z, MEH0+Z) #= MEH1,
max(MSF0, MEH1) #= MSF1,
zs_max_(Zs, MEH1, MSF1, MSF).
First, the sample queries from the original solution that yield the same results:
?- zs_max([-2,1,-3,4,-1,2,1,-5,4], Max).
Max = 6
?- zs_max([-2,3,4,-5,8,-12,100,-101,7], Max).
Max = 100
However this version is more general, in that it works with arbitrary values (as suggested by #false in the comment to solution). This is accomplished by starting with the value of the first element of the list instead of 0. Thus the following query yields a different result:
?- zs_max([-2,-3,-4], X).
X = -2
?- zs_maxmum([-2,-3,-4], X).
X = 0
Another difference is that the empty list has no solution:
?- zs_max([], X).
no
?- zs_maxmum([], X).
X = 0
I think this behaviour is more reasonable, as the empty list has no sublist and hence no sums of sublists from which to choose a maximum. However, if desired, a special case for the empty list can be easily added:
zs_max([], replaceThisWithAReasonableValue).
the standard way is to add an output parameter, that gets unified when the recursion stops. Something like
max_sum(L, S) :-
max_sum(L, 0, 0, S).
max_sum([], _, S, S).
...
Then, your code is way more complex than needed: both versions listed on Wikipedia don't require any test, or length/2 computation.
Try to simplify it leaving just the computation (you can use for instance Max_ending_here is max(0, H + X), and the tail recursive call.

Resources