I'm new to Prolog. I managed to learn C and Java relatively quickly but and Prolog is giving me a lot of trouble. My trouble is understanding lists and writing functions? For example. We have this automaton:
I can do this task in C and Java, no problems. But the course wants Prolog. With my current knowledge I could do things like this:
% 1. Check whether all integers of the list are < 10.
less_than_10([]).
less_than_10([Head|Tail]) :-
Head < 10,
less_than_10(Tail).
Just so you know where my knowledge is at. Very basic. I did read the list chapter in Learn Prolog Now but it's still confusing me. They gave us a hint:
Every node should be presented like:
delta(1, d, 2)
% or
alpha(2, a, 2)
They also told us to pass the list in questions to a predicate that returns true if the list fits the automaton and false if not:
accept([d,a,b,a,b,b,b,c,d,c]).
The output is true.
Where to go from here? I'm guessing the first step is to check if the Head of the list is 1. How do I do that? Also, should I add every node as fact into the knowledge base?
So that's pretty easy. Super-direct, much more than if you were using C or Java.
Let's write an interpreter for this graph that:
Is given a list of named transitions ;
Walks the transitions using the given graph along a path through that graph ;
Accepts (Succeeds) the list if we end up at a final state ;
Rejects (Fails) the list if we do not ;
And.. let's say throws an exception if the list cannot be generated by the given graph.
Prolog gives us nondeterminism for free in case there are several paths. Which is nice.
We do not have an class to describe the automaton. In a sense, the Prolog program is the automaton. We just have a set of predicates which describe the automaton via inductive definitions. Actually, if you slap a module definition around the source below, you do have the object.
First describe the graph. This is just a set of Prolog facts.
As required, we give the transitions (labeled by atoms) between nodes (labeled by integers), plus we indicate which are the start and end nodes. There is no need to list the nodes or edges themselves.
delta(1,d,2).
delta(2,a,2).
delta(2,b,2).
delta(2,d,4).
delta(2,e,5).
delta(2,c,3).
delta(3,d,6).
delta(6,c,5).
start(1).
end(4).
end(5).
A simple database. This is just one possible representation of course.
And now for the graph walker. We could use Definite Clause Grammars here because we are handling a list, but lets' not.
First, a predicate which "accepts" or "rejects" a list of transitions.
It looks like:
% accepts(+Transitions)
It starts in a start state, then "walks" by removing transitions off the list until the list is empty. Then it checks whether it is at an end state.
accepts(Ts) :- % accept the list of transitions if...
start(S), % you can accept the list starting
accepts_from(S,Ts). % from a start state
accepts_from(S,[T|Ts]) :- % accepts the transitions when at S if...
delta(S,T,NextS), % there is a transition S->NextS via T
accepts_from(NextS,Ts). % and you can accept the remaining Ts from NextS. (inductive definition)
accepts_from(S,[]) :- % if there is no transition left, we accept if...
end(S). % we are a final state
Ah, we wanted to throw if the path was impossible for that graph. So a little modification:
accepts(Ts) :- % accept the list of transitions if...
start(S), % you can accept the list starting
accepts_from(S,Ts). % from a start state
accepts_from(S,[T|Ts]) :- % accepts the transitions when at S if...
delta(S,T,NextS), % there is a transition S->NextS via T
accepts_from(NextS,Ts). % and you can accept the remaining Ts from NextS.
accepts_from(S,[T|Ts]) :- % accepts the transitions when at S if...
\+ delta(S,T,NextS), % there is NO transition S->NextS via T
format(string(Txt),"No transition at ~q to reach ~q",[S,[T|Ts]]),
throw(Txt).
accepts_from(S,[]) :- % if there is no transition left, we accept if...
end(S). % we are a final state
And so:
?- accepts([d,a,b,a,b,b,b,c,d,c]).
true ; % yup, accepts but maybe there are other paths?
false. % nope
?- accepts([d,a,a,a,a,e]).
true ;
false.
?- accepts([d,a,a,a,a]).
false.
?- accepts([d,c,e,a]).
ERROR: Unhandled exception: "No transition at 3 to reach [e,a]"
The above code should also be able to find acceptable paths through the graph. But it does not:
?- accepts(T).
... infinite loop
This is not nice.
The primary reason for that is that accept/2 will immediately generate an infinite path looping at state 2 via transitions a and b. So one needs to add a "depth limiter" (the keyword is "iterative deepening").
The second reason would be that the test \+ delta(S,T,NextS) would succeed at node 4 for example (because there is nowhere to go from that node) and cause an exception before trying out the possibility of going nowhere (the last clause). So when generating, throwing is a hindrance, one just wants to reject.
Addendum: Also generate
The following only accepts/rejects and does not throw, but can also generate.
:- use_module(library(clpfd)).
accepts(Ts,L) :- % Accept the list of transitions Ts of length L if
start(S), % ...starting from a start state S
accepts_from(S,Ts,L). % ...you can accept the Ts of length L.
accepts_from(S,[T|Ts],L) :- % Accept the transitions [T|Ts] when at S if
(nonvar(L)
-> L >= 1
; true), % L (if it is bound) is at least 1 (this can be replaced by L #> 0)
delta(S,T,SN), % ...and there is a transition S->SN via T
Lm #= L-1, % ...and the new length is **constrained to be** 1 less than the previous length
accepts_from(SN,Ts,Lm). % ...and you can accept the remaining Ts of length Lm from SN.
accepts_from(S,[],0) :- % If there is no transition left, length L must be 0 and we accept if
end(S). % ...we are a final state.
delta(1,d,2).
delta(2,a,2).
delta(2,b,2).
delta(2,d,4).
delta(2,e,5).
delta(2,c,3).
delta(3,d,6).
delta(6,c,5).
start(1).
end(4).
end(5).
generate :-
between(0,7,L),
findall(Ts,accepts(Ts,L),Bag),
length(Bag,BagLength),
format("Found ~d paths of length ~d through the graph\n",[BagLength,L]),
maplist({L}/[Ts]>>format("~d : ~q\n",[L,Ts]),Bag).
And so:
?- accepts([d,a,b,a,b,b,b,c,d,c],_).
true ;
false.
?- accepts([d,a,a,a,a],_).
false.
?- accepts([d,c,e,a],_).
false.
?- generate.
Found 0 paths of length 0 through the graph
true ;
Found 0 paths of length 1 through the graph
true ;
Found 2 paths of length 2 through the graph
2 : [d,d]
2 : [d,e]
true ;
Found 4 paths of length 3 through the graph
3 : [d,a,d]
3 : [d,a,e]
3 : [d,b,d]
3 : [d,b,e]
true ;
Found 9 paths of length 4 through the graph
4 : [d,a,a,d]
4 : [d,a,a,e]
4 : [d,a,b,d]
4 : [d,a,b,e]
4 : [d,b,a,d]
4 : [d,b,a,e]
4 : [d,b,b,d]
4 : [d,b,b,e]
4 : [d,c,d,c]
true
Here's my answer. I sought to completely separate the data from the logic.
There are rules to infer the possible paths, start and end nodes.
The edge/2 predicate stands for either an alpha or a delta line.
The path (DCG) predicate describes a list of edges that ends with an end node.
The start and end nodes are inferred using the start_node/1 and end_node/1 predicates.
Finally, the phrase/3 is used to describe the list of paths that are valid automata.
delta(1, d, 2).
delta(2, d, 4).
delta(2, e, 5).
delta(2, c, 3).
delta(3, d, 6).
delta(6, c, 5).
alpha(2, a, 2).
alpha(2, b, 2).
edge(Node, Node, Via) :-
alpha(Node, Via, Node).
edge(From, To, Via) :-
delta(From, Via, To).
path(From, To) -->
{ end_node(To),
dif(From, To),
edge(From, To, Via)
},
[Via].
path(From, To) -->
{edge(From, Mid, Via)},
[Via],
path(Mid, To).
start_node(Node) :-
node_aux(start_node_aux, Node).
end_node(Node) :-
node_aux(end_node_aux, Node).
start_node_aux(Node) :-
edge(Node, _, _),
\+ edge(_, Node, _).
node_aux(Goal, Node) :-
setof(Node, call(Goal, Node), Nodes),
member(Node, Nodes).
end_node_aux(Node) :-
edge(_, Node, _),
\+ edge(Node, _, _).
automaton -->
{start_node(Start)},
path(Start, _End).
accept(Steps) :-
length(Steps, _N),
phrase(automaton, Steps).
I suspect that David did not use Definite Clause Grammars because you should be familiar with the basics before learning DCGs.
I'm a very newbie to Prolog and I already need help. I looked up other similar questions but it didn't answer my question.
The problem is;
I have a list of mixed elements [Y, rat, gorilla, 30, mother(alex)]. I want to make a new list out of this with exclusively atoms.
So query should look like this.
?- atoms([Y, rat, gorilla, 30, mother(alex)], Result).
Result = [rat, gorilla].
I tried but I have no idea how to solve this. I think it should be recursive because it needs to check each item weather it's an atom or not.
atoms([], []).
atoms([H | T], Result) :-
atom(H),
append(H, [], Result).
What you want to do is called "filtering" and there is a ready-made "higher-level predicate" for this already. Why "higher level"? Because it doesn't deal in first-order "objects" only, but takes an executable goal that it calls.
Note that this is an eminently functional approach to programming and there is nothing wrong with that: fat chunks of a "logic program" are actually written in functional style. Here we go:
In SWI-Prolog, the predicate that filters is called include/3 or exclude/3.
% atoms/2 filters list Li into list Lo using the predicate atom/1
% This only works in direction Li-->Lo.
atoms(Li,Lo) :- include(atom,Li,Lo).
And a bit of unit test code:
:- begin_tests(filtering).
test("basic test", true(Result = [rat, gorilla])) :-
atoms([Y, rat, gorilla, 30, mother(alex)], Result).
:- end_tests(filtering).
And so:
?- run_tests.
% PL-Unit: filtering . done
% test passed
true.
It works.
Of course, you can always write your own atoms/2 using a recursive call (aka. using an inductive definition)
atoms_i([], []).
atoms_i([H|T], [H|Result]) :- % retain the H in the result list
atom(H), % the "guard" passes if H is atom
!, % then we commit to this branch
atoms_i(T, Result).
atoms_i([H|T], Result) :- % do not retain H in the result list
\+atom(H), % the "guard" passes if H is not atom
!, % then we commit to this branch
atoms_i(T, Result).
People will say that you can leave out the \+atom(H),! in the third clause for efficieny reasons. Although they are right, I find doing that extremely annoying as I prefer symmetry in the source code and cuts that can in principle be removed at a whim. Plus it's about time the compiler start doing some work to find that efficiency itself. It's 2020, not 1980.
Let's add a bit of unit test code:
:- begin_tests(filtering_i).
test("basic test", true(Result = [rat, gorilla])) :-
atoms_i([Y, rat, gorilla, 30, mother(alex)], Result).
:- end_tests(filtering_i).
And so:
?- run_tests.
% PL-Unit: filtering_i . done
% test passed
true.
Good.
I am stuck for this problem...
isAt(keys, room3).
isAt(book, room3).
isAt(keys, room6).
isAt(keys, room4).
currently, room3 have keys and book.
I want to print keys and book.
I tried this code and apparently prints only one. (just keys)
look :- isIn(Location),
write('You are in '),
write(Location),
nl,
items_inroom(Location),
nl.
items_inroom(Location) :-
isIn(Location),
isAt(Item, Location),
write('Available Item(s):'),
write(Item),
nl.
items_inroom(_) :-
write('Available Item(s): None'),
nl.
items_inroom is the code that trying to print all these facts.
How can I approach this?
any help will be great! Thank you.
From Chapter 11 in "The Craft of Prolog" by Richard O'Keefe, a bit simplified/refactored to save keystrokes:
print_item_report(Location) :-
( setof(Item, isAt(Item, Location), Items)
-> format("Items available in ~w:~n", [Location]),
forall(member(I, Items),
format("~w~n", [I]))
% print_item_report_footer
; format("No items in ~w~n", [Location])
).
% etc
If you don't have format for whatever reason, you can still use write. If you don't have forall, then this:
forall(Condition, Action)
is defined as
\+ (Condition, \+ Action )
so you can use that instead. See the SWI-Prolog forall/2 documentation for details.
Find all items and display them.
items_inroom(Location) :-
write('Available Item(s):'),
findall(Item, isAt(Item, Location), Items),
show_items(Items).
show_items([]) :-
write('None'), !.
show_items(Items) :-
write(Items).
Actually you can implement the show_items(Items) in any way you want.
items_inroom/1 predicate will always print the first occurrence of Item on all facts isAt/2. You need to loop over all the facts isAt/2, use a metapredicate setof/3, bagog/3 or findall/3, I will recomend setof/3 like #Boris did, or build your own bucle (maybe not the best idea, but it's an option):
show_items(Location):- isAt(Item, Location), % Condition
write(Item), nl, % Process result
fail. % force backtracking to evaluate condition and find a new result
show_items(_). % return true when all options have been evaluated
I don't have much experience in Prolog.
add(first, second) :-
see('telefon_375319.txt'),
read(L),
seen,
M = [[first, second] | L],
tell('telefon_375319.txt'),
write(M),
nl,
told.
The purpose of the above code is to:
Read the list (which is a list of list) from a file (into L).
Add the list [first, second] at the beginning of the list (it might be at the end or even in the middle).
Save it to a file.
The result I get in the file is following:
[[x, y], end_of_file].
The result should be:
[[x, y], and here should be the rest of the list].
I don't know why ,but Prolog read a text end_of_file into L instead of the list which is stored in the file.
What's wrong?
end_of_file is not text. It’s atom and it’s read when the file ends. But in my SWI Prolog I see that an empty line must me placed at the end of the file in order to read it correctly. Searching for relevant info on that.
I modified the original slightly to use variables, not atoms. I also added writing a period (.) after the list that's written since read expects to see it:
add(First, Second) :-
see('telefon_375319.txt'),
read(L),
seen,
M = [[First, Second] | L],
tell('telefon_375319.txt'),
write(M), write('.'),
nl,
told.
Contents of telefon_375319.txt:
[[a,b],[c,d]].
Then run:
| ?- add(x, y).
yes
New contents of telefon_375319.txt:
[[x,y],[a,b],[c,d]].
Run:
| ?- add(z, w).
New contents of telefon_375319.txt:
[[z,w],[x,y],[a,b],[c,d]].
Other than the two changes that I made which I mentioned above, I don't see anything wrong with the predicate based upon the described desired behavior. I ran this on GNU Prolog and SWI Prolog on Linux, and SWI Prolog on Windows 7 and it worked as expected in all cases. The text file ends in a single \n in the case of Linux, and \r\n in the case of Windows.
How can I check if an element in the list is an empty list: [] ?
I've got the following:
display_degrees([A,B,C,D]):- write(B).
display_degrees([A,B,C,D]):- B==[], nl,write('has no degree'), nl, !.
When I enter in something like:
display_degrees([1,[],3,4]).
I just get: [] instead of 'has no degree'. Is my syntax wrong? Can I not add a clause to this predicate like this?
You're getting this behavior because proof search stops when a goal has succeeded. When you type
display_degrees([1,[],3,4]).
the first rule unifies, and it writes B. Since it was a success, it stops. You can ask Prolog to keep searching, and then it will find the second clause. In swipl, I get
?- [foo].
?- display_degrees([1,[],3,4]).
[]
true r % I type 'r' there
has no degree
true.
If you're just learning Prolog, I suggest you avoid the cut operator ! for some time. Also, doing IO is not the most intuitive thing. I would try some exercises with defining things like natural numbers and recursive functions. E.g., plus:
plus(z, X, X).
plus(s(X), Y, s(Z)) :- plus(X, Y, Z).
The problem with what you have is that the more general rule will fire first. You could switch the order:
display_degrees([A,[],C,D]) :- nl, write('has no degree'), nl, !.
display_degrees([A,B,C,D]) :- write(B).
I could just as well have written for the first predicate:
display_degrees([A,B,C,D]) :- B == [], nl, write('has no degree'), nl, !.
But the "shortcut" I show initially is more idiomatic for a Prolog predicate like this.
I kept the cut since you know you deterministically want one choice. The first rule will match if and only if the second list element is [].
| ?- display_degrees([1,[],3,4]).
has no degree
yes
| ?- display_degrees([1,2,3,4]).
2
yes
| ?-