how to print the whole route - prolog

Such as for go(a, d). i want it to print the route as well such as route a, route b, route, c and route d
door(a, b).
door(b, c).
door(c, d).
door(b, e).
door(e, f).
door(e, g).
go(FromRoom, ToRoom):-
door(FromRoom,ToRoom).
go(FromRoom, ToRoom) :-
door(FromRoom, NextRoom),
go(NextRoom, ToRoom).

A good way is to simply turn go/2 into a relation that also takes the route into account. As is often the case when describing lists, DCGs are a good fit:
go(From, To) --> [From, To], { door(From, To) }.
go(From, To) --> [From],
{ door(From, Next) },
go(Next, To).
Example:
?- phrase(go(a, d), Rooms).
Rooms = [a, b, c, d] ;
false.
And regarding write/1: This is rarely necessary because we can often let the toplevel take care of printing answers. format/2 is often a better fit than write/1 for formatting output if needed, for example, instead of:
write('['), write(From), write(':'), write(To), write(']')
you can write:
format("[~w: ~w]", [From, To])

Please try not to mix IO and logic in your predicates. It will make your code hard to test, debug, reason about and will produce very confusing output on backtraking.
Here you can keep the road in a list during the recursion.
An example would be, with a third argument:
go(FromRoom, ToRoom, [FromRoom, ToRoom]) :-
door(FromRoom, ToRoom).
go(FromRoom, ToRoom, [FromRoom|Path]) :-
door(FromRoom, NextRoom),
go(NextRoom, ToRoom, Path).
A query correctly returns the path:
?- go(a, g, Path).
Path = [a, b, e, g] ;
false.
If necessary, you can then format the output list Path when you output it. But it's now a really simpler problem: format a list instead of output things during recursion.

You need to use the write predicate for this as follows
go(FromRoom, ToRoom):-
door(FromRoom,ToRoom),
write('['), write(FromRoom), write(':'), write(ToRoom), write(']').
go(FromRoom, ToRoom) :-
door(FromRoom, NextRoom),
write('['), write(FromRoom), write(':'), write(NextRoom), write(']'),
go(NextRoom, ToRoom).
Alternatively, the route in the format you wanted is
go(FromRoom, ToRoom):-
door(FromRoom,ToRoom),
write(FromRoom), write(' route '), write(ToRoom).
go(FromRoom, ToRoom) :-
door(FromRoom, NextRoom),
write(FromRoom), write(' route '),
go(NextRoom, ToRoom).

Related

Prolog: find and put into the list duplicates

Good day, guys. Can't figure out, why prolog predicate is putting all duplicates into my new list. F.e. I have to pick all duplicates:
?- duplicates([a, b, a, a, d, d], R).
R = [a, d]
I have wrote this prolog program:
duplicates([], []).
duplicates([First|Rest], NewRest) :-
not(member(First, Rest)),
duplicates(Rest, NewRest).
duplicates([First|Rest], [First|NewRest]) :-
member(First, Rest),
duplicates(Rest, NewRest).
But it returns:
R = [a, a] .
I think I need to put a (!) sign somewhere, but cannot understand, where. Any suggestions?
library(aggregate) allows a compact solution for your problem.
duplicates(L,D) :- findall(K,(aggregate(count,member(K,L),C),C>1),D).
?- duplicates([a, b, a, a, d, d], R).
R = [a, d].
Clearly, it's less immediate to grasp than #Raubsauger' good answer, and not available in every Prolog out there.
Try this:
duplicates([], []).
duplicates([First|Rest], NewRest) :-
\+ member(First, Rest),
duplicates(Rest, NewRest).
duplicates([First|Rest], NewRest) :-
duplicates(Rest, NewRest),
member(First, NewRest).
duplicates([First|Rest], [First|NewRest]) :-
member(First, Rest),
duplicates(Rest, NewRest),
\+ member(First, NewRest).
?- duplicates([a, b, a, a, d, d], R).
R = [a, d] ;
false.
I have choosen a version where you don't need cuts (the !). Also I added another rule: elements in NewRest can appear only once. Note: membership in NewRest can be testet only after unification of all of its entries.

Creating a Prolog predicate via macro-like meta programming

I need a way for this to work:
?- create_pred(f, [A, B], (write(That), write(B), write(A), write(This))).
true.
?- f(this, a, b).
_L154bathis
true.
I have it working without the This so far:
?- create_pred(f, [A, B], (write(That), write(B), write(A))).
true.
?- f(a, b).
_L154ba
true.
The code for that is the following:
create_pred(Name, Args, Body) :-
length(Args, Argc),
functor(F, Name, Argc),
term_variables(F, Vars),
term_variables(Args, Vars),
assertz((F :- Body)).
Any idea how I could get that first argument unified with the This (and not the That) variable from the body?

Use findall with arg

I'm learning Prolog and I try to rewrite the univ predicate:
?- foo(hello, X) =.. List.
List = [foo, hello, X]
?- Term =.. [baz, foo(1)].
Term = baz(foo(1))
I already wrote a first version that works well:
get_args(_, Arity, Arity, []).
get_args(T, Arity, N, [Arg|Args]) :-
I is N + 1,
arg(I, T, Arg),
get_args(T, Arity, I, Args).
univ(T, [Functor|Args]) :-
length(Args, Arity),
functor(T, Functor, Arity),
get_args(T, Arity, 0, Args),
!
I wanted to try another way to implement it. So, I rewrite this one by using findall and arg:
univ(T, [Functor|Args]) :-
length(Args, Arity),
functor(T, Functor, Arity),
findall(Arg, arg(_, T, Arg), Args),
!.
This one doesn't work well. Here is an example:
?- univ(a(C, D, E), L).
L = [a, _G1312, _G1315, _G1318].
?- univ(T, [a, C, D, E]).
T = a(_G1325, _G1326, _G1327).
Thus, I have a simple question: is it possible to use arg with findall in order to retrieve the name of each arguments?
As noted by #mat, findall/3 copies variables. You can use bagof/3 instead:
univ(T, [Functor|Args]) :-
length(Args, Arity),
functor(T, Functor, Arity),
bagof(Arg, I^arg(I, T, Arg), Args),
!.
?- univ(a(C, D, E), L).
L = [a, C, D, E].
This different behaviour wrt findall/3 and setof/3 can also be useful when you need to handle attributed variables.

testing in the console

The consult ?- go(c, g). returns false, but states true for ?- go(a, d).. I do not actually understand it, as I have added the proper rules, and for most of them it works.
Here are the statements I'm using:
door(a, b).
door(b, c).
door(c, d).
door(b, e).
door(e, f).
door(e, g).
go(FromRoom, ToRoom):-
door(FromRoom,ToRoom).
go(FromRoom, ToRoom):-
door(ToRoom, FromRoom).
go(FromRoom, ToRoom) :-
door(FromRoom, NextRoom),
go(NextRoom, ToRoom), !.
go(FromRoom, ToRoom):-
door(ToRoom,NextRoom),
go(NextRoom, FromRoom), !.
You need to add accumulator to your predicate to avoid got stuck in inf loop.
door(a, b).
door(b, c).
door(c, d).
door(b, e).
door(e, f).
door(e, g).
go(From, To, T) :-
T=[From|T1],
go(From, To, [To], T1).
go(From, To,T, T) :-
door(From, To),!.
go(From,To,T,T) :-
door(To,From),!.
go(From, To, Acc,T) :-
door(X, Do),!,
\+ member(X, Acc),
go(From, X, [X|Acc],T).
go(From,To,Acc,T) :-
door(X,Z),!,
\+member(Z,Acc),
go(X,To,[X|Acc],T).
You fourth rule should read
go(FromRoom, ToRoom):-
door(ToRoom,NextRoom),
go(FromRoom,NextRoom),!.
because you already swapped the search order in calling door/2, but this leads to looping, as rightly remarked by #whd. Here a version that 'returns' the path
go(Room, Room, Path, Path).
go(FromRoom, ToRoom, SoFar, Path) :-
( door(FromRoom, NextRoom) ; door(NextRoom, FromRoom) ),
\+ member(NextRoom, SoFar),
go(NextRoom, ToRoom, [NextRoom|SoFar], Path).
test:
?- go(c,g,[],P).
P = [g, e, b, c, d] ;
P = [g, e, b] ;
false.
Due to Prolog search order, it 'takes a tour' in d before going back. It's easy to avoid acting on the SoFar parameter at top level call...

Prolog string manipulation

I have a string like 'pen,pencil,eraser'. How can I make this predicate.
things(pen,pencil,eraser).
Do you have any idea? ( I use prolog)
Here's a small example of specialized Prolog code for your problem which should work on most implementations (not only SWI-Prolog, but GNU Prolog, SICStus, etc.):
make_term(Functor, StringArgs, Term) :-
split_atom(StringArgs, ',', Args),
Term =.. [Functor|Args].
split_atom(A, E, L) :-
atom_chars(A, C),
split_atom2(C, E, L).
split_atom2([], _, []).
split_atom2(C, E, [A|L]) :-
append(C0, [E|C1], C), !,
atom_chars(A, C0),
split_atom2(C1, E, L).
split_atom2(C, _, [A]) :-
atom_chars(A, C).
Testing it out:
?- make_term(things, 'pen,pencil,eraser', T).
T = things(pen, pencil, eraser).
if you use swi-prolog, you can create this first: 'things(pen,pencil,eraser)' and then use term_to_atom/2
so something like:
get_term(Term):-
atom_concat('things(','pen,pencil,eraser',Temp),
atom_concat(Temp,')',A),
term_to_atom(Term, A).

Resources