Prolog confusion. Can someone explain how minimal works? - prolog

I understand the code up to minimal but after that there's too many variables and I keep losing track of what each one is doing. If someone could explain it or do it with renamed variables that would be an amazing help, as I think this code will probably come up on my Christmas exam and I want to be able to explain what's going on.
road(a, b, 1).
road(b, c, 1).
road(a, c, 13).
road(d, a, 1).
/*Getting from A to B through a list of places R in N kms*/
route(A,B,R,N) :- travel(A,B,[A],Q,N), reverse(Q,R).
/*Travelling from A to B through P a list of towns B|P of distance L*/
travel(A,B,P,[B|P],Dist) :- road(A,B,Dist).
/*Travelling from A to B through Visited, on your Route R with distance Distance*/
/*Find if there is a road from A to b and store the distance. Make sure C is not equal to B*/
travel(A,B,Visited,R,Distance) :-
road(A,C,Dist), C \== B,
/*Make sure C is not in Visited to avoid infinite loop,
use recursion to find the full route and distance */
\+member(C,Visited), travel(C,B,[C|Visited],R,Dist1), Distance is Dist + Dist1.
/*Find the shortest route from A to B*/
shortest(A,B,R,N) :-
setof([Route,Dist],route(A,B,Route,Dist),Set),
Set = [_|_], minimal(Set,[R,N]).
minimal([F|R],M) :- min(R,F,M).
/*The shortest path*/
min([],M,M).
min([[P,L]|R],[_,M],Min):- L < M, !, min(R,[P,L],Min).
min([_|R],M,Min) :- min(R,M,Min).

since setof gives a sorted list of solutions, it's sufficient to produce solutions of appropriate 'shape', placing first the value you want to minimize: try
shortest(A,B,R,N) :-
setof((Dist,Route), route(A,B,Route,Dist), [(N,R)|_]).

Related

Can anyone explain to me what this Prolog code is doing?

So I have a question I need to answer commenting on how this code is executed and what it is doing and I am not sure what is going on in it. I think it is something to do with if the values appear in the list? No idea however any help is appreciated!!
I also need to explain what the output of this query is regarding the code below p([5,3,6,3,7],L,4,7).
p([],[],_).
p([H|T],[H|R],N,M) :-H >= N, H =< M, p(T,R,N,M).
p([H|T],R,N,M) :- H < N, p(T,R,N,M).
p([H|T],R,N,M) :- H > M, p(T,R,N,M).
(Let's assume the first line has a typo and should have two _ in it rather than one).
Instead of trying to unpick the variables and follow them through, step back and look at the shape, and the general patterns; first note the pattern of how the predicates are all p/4 and start like this:
p([],
p([H|T],
p([H|T],
p([H|T],
It's common to put lists in the first position, as Prolog systems tend do more optimisation on the first position, and this is a common pattern for a recursive list walk down to the empty-list end of a list.
Then note that the last two lines have the same header and the same recursive call:
p([H|T],R,N,M) :- , p(T,R,N,M).
p([H|T],R,N,M) :- , p(T,R,N,M).
And the recursive call changes [H|T] into T, so yes, recursive list walk looks likely. Bit suspicious that they have the same call at the end in both cases. What's different about them, and why does it matter?
H < N,
H > M,
It is common to write if/else like this, as two separate rules, one rule for each case, e.g. a cutoff value, one for being above it and another for beign below it. This is not that because both rules describe the same behaviour; H is being compared with N in one rule and M in the other. H is outside either end of a boundary, do nothing?
What's the second line?
p([H|T],[H|R],N,M) :- H >= N, H =< M, p(T,R,N,M).
It has the same recursive call but it has a different header where H merges into the second parameter, and the conditions are H >= N, H =< M; that's H being inside the boundary.
So, it's a filter; in p([5,3,6,3,7],L,4,7) the list elemetns between N and M (inclusive) get merged into L, the rest are ignored. It's very similar to:
?- include(between(4, 7), [5,3,6,3,7], L).

Prolog sum distance between list of nodes ids

node(1,22,44).
node(2,43,53).
node(3,23,12).
distance(Id1,Id2,D) :-
node(Id1,X1,Y1),
node(Id2,X2,Y2),
D is sqrt((X2-X1)^2 + (Y2-Y1)^2).
distancePath([X,Y],D) :-
distance(X,Y,D).
distancePath([H,X|T],Distance) :-
distancePath([X|T],Rest),
distance(H,X,D),
Aux is Rest + D,
Distance is Distance + Aux.
I have some kind of problem on the distancePath because when i run distancePath([1,2,3],Distance). it give me "Instantiation error in argument 2 of (is)/2" can someone help me?
Distance is Distance + Aux. does not make much sense. Variables are immutable, this means once set the variable will keep the same value in that branch of the Prolog evaluation. Furthermore Distance is unbounded, hence the error.
You actually already have the value for Distance: it is Aux since it is the distance for the Rest of the list, plus the distance D:
distancePath([X,Y], D) :-
distance(X,Y,D).
distancePath([H,X|T], Aug) :-
distancePath([X|T],Rest),
distance(H,X,D),
Aux is Rest + D.
This then gives us:
?- distancePath([1,2,3], D).
D = 68.4652982294199 ;
false.
The above is however not very efficient: it does not make use of tail recursion, and furthermore you perform unpacking of the cons constructor multiple times. You can make use of a helper predicate to boost efficiency:
distancePath([X|T], Result) :-
distancePath(T, X, 0, Result).
distancePath([], _, D, D).
distancePath([J|T], I, Di, Do) :-
distance(I, J, Dd),
D2 is Di + Dd,
distancePath(T, J, D2, Do).

List all reachable nodes

I have a predicate that is bi-directional and tells, whether one node is connected to another.
E.g.
has(a, b).
has(b, c).
has(d, b).
Now I would like to list all nodes which can be reached (from a specific node) with a given number of steps.
connected_nodes(a, T, 2).
should therefore output
T = c
T = d
My current code looks like this:
connected_nodes(A, B, 0) :- write(A).
connected_nodes(A, B, N) :-
N > 0, M is N - 1,
has(A, X),
connected_nodes(X, B, M).
This works for T = c but not for T = d, as this is a bi-directional relation.
Am I thinking correctly in terms of recursion or how in which other way does this problem have to be solved?
Your connected predicate seems fine. It might have made more sense to make the base case rely on has, but it's up to you.
However, you say your has predicate is "bi-directional" but you haven't explicitly created a rule that indicates this.
The following code is a "bi-directional" version of your code:
has(a, b).
has(b, c).
has(d, b).
bi_has(X,Y) :- has(X,Y).
bi_has(X,Y) :- has(Y,X).
connected_nodes(A, B, 0) :- write(A).
connected_nodes(A, B, N) :-
N > 0, M is N - 1,
bi_has(A, X),
connected_nodes(X, B, M).
Note however that your query now returns a as a possible answer too. This makes since, because you never explicitly stated you don't want to return back to the same element after 2 steps.
If you don't want this behaviour, then you need to also keep a list of elements visited so far (i.e. an accumulator), and confirm that you're not revisiting elements.

update nth element of a list

I am new to prolog , I have a list in prolog like A=[1,2,3,4], and than I accessed nth element using nth(N,[_|T],R). Now I have Nth element in R, than I have done some calculation on R. Now what I want is to update that nth element in list.
Because of I am doing a lot of calculations with each element in list I can't make a new list each time.
I didn't find any method to update list.
With regard to our conversation, you can add two lists together, creating a third, by specifying that the two head elements of the source lists, added together, make the head element of the result list, and that this applies to the remainder of the lists.
There is also a need for a base case, that is, when the two source lists are empty, so should the result list.
addLists([X|A], [Y|B], [Z|C]) :- Z is X+Y, addLists(A, B, C).
addLists([], [], []).
Remember you are always aiming to specify the constraints of the answer, more than the method of answering it. Prolog is very different to other programming languages in that you do not tell it how to do something, you simply tell it conditions that are true for the answer and let it extrapolate it.
From the comments you exchanged with #Orbling seems that what you need is a kind of maplist/4
process_list(A, B, C) :-
maplist(process_elem, A, B, C).
process_elem(A, B, C) :- C is A + B. % or whatever needed
If you are using the index in process_elem then this is not appropriate. Then make a recursive visit of list, passing down the index
process_list(A, B, C) :-
process_list(1, A, B, C).
process_list(I, [A|As], [B|Bs], [C|Cs]) :-
C is A + B * I, % or whatever needed
J is I + 1,
!, process_list(J, As, Bs, Cs).
process_list(_, [], [], []).
edit Just to add to the various ways exposed in answers to the question #Orbling suggests, here a way using nth0/4
?- I = 6, nth0(I,"hello world",_,T), nth0(I,U,0'W,T), format('~s',[U]).
hello World

Simplified Travelling Salesman in Prolog

I've looked through the similar questions but can't find anything that's relevant to my problem. I'm struggling to find an algorithm or set of 'loops' that will find a path from CityA to CityB, using a database of
distance(City1,City2,Distance)
facts. What I've managed to do so far is below, but it always backtracks at write(X), and then completes with the final iteration, which is what I want it to do but only to a certain extent.
For example, I don't want it to print out any city names that are dead ends, or to use the final iteration. I want it to basically make a path from CityA to CityB, writing the name of the cities it goes to on the path.
I hope somebody can help me!
all_possible_paths(CityA, CityB) :-
write(CityA),
nl,
loop_process(CityA, CityB).
loop_process(CityA, CityB) :-
CityA == CityB.
loop_process(CityA, CityB) :-
CityA \== CityB,
distance(CityA, X, _),
write(X),
nl,
loop_process(X, CityB).
I tried to demonstrate how you can achieve what you're working on so that you can understand better how it works. So since your OP wasn't very complete, I took some liberties ! Here are the facts I'm working with :
road(birmingham,bristol, 9).
road(london,birmingham, 3).
road(london,bristol, 6).
road(london,plymouth, 5).
road(plymouth,london, 5).
road(portsmouth,london, 4).
road(portsmouth,plymouth, 8).
Here is the predicate we will call to find our paths, get_road/4. It basically calls the working predicate, that has two accumulators (one for the points already visited and one for the distance we went through).
get_road(Start, End, Visited, Result) :-
get_road(Start, End, [Start], 0, Visited, Result).
Here is the working predicate,
get_road/6 : get_road(+Start, +End, +Waypoints, +DistanceAcc, -Visited, -TotalDistance) :
The first clause tells that if there is a road between our first point and our last point, we can end here.
get_road(Start, End, Waypoints, DistanceAcc, Visited, TotalDistance) :-
road(Start, End, Distance),
reverse([End|Waypoints], Visited),
TotalDistance is DistanceAcc + Distance.
The second clause tells that if there is a road between our first point and an intermediate point, we can take it and then solve get_road(intermediate, end).
get_road(Start, End, Waypoints, DistanceAcc, Visited, TotalDistance) :-
road(Start, Waypoint, Distance),
\+ member(Waypoint, Waypoints),
NewDistanceAcc is DistanceAcc + Distance,
get_road(Waypoint, End, [Waypoint|Waypoints], NewDistanceAcc, Visited, TotalDistance).
Usage is as follows :
?- get_road(portsmouth, plymouth, Visited, Distance).
And yields :
Visited = [portsmouth, plymouth],
Distance = 8 ;
Visited = [portsmouth, london, plymouth],
Distance = 9 ;
Visited = [portsmouth, plymouth, london, plymouth],
Distance = 18 ;
false.
I hope it will be helpful to you.
Please separate the pure part from the impure (I/O, like write/1, nl/0 but also (==)/2 and (\==)/2). As long as they are entirely interlaced with your pure code you cannot expect much.
Probably you want a relation between a starting point, an end point and a path in between.
Should that path be acyclic or do you permit cycles?
To ensure that an element X does not occur in a list Xs use the goal maplist(dif(X),Xs).
You do not need any further auxiliary predicates to make this a nice relation!
You should return a successful list as an Out variable in all_possible_paths. Then write out that list. Don't do both in the same procedure.

Resources