Learning Prolog: Lab #7
Today's lab introduced lists, and they are a lot harder to comprehend than the other things that we have done so far. You can define a list in Prolog with square brackets, like so:
?- Sandwiches = [ cheese, chicken, cucumber, lettuce_and_tomato ].
This isn't particularly useful, but the real fun comes later on after you've written a bunch of utility rules. Before I get into that though, I should explain a little bit about heads and tails. In Prolog, you can split a list up into its head and its tail with a bar character (|
), like so:
printhead([ Head | Tail ]) :-
write(head).
Here's a quick example of the above in action:
?- printhead([ apple, orange, grape ]).
apple
true.
This is especially useful for recursion, as we shall see in a moment. With that out of the way, here are few utility rules that I put together. There's one that writes out all the items in a list, one that counts up all the numbers in a list, one to concatenate two lists together, and one to calculate the length of a list. They all use loops, so if you don't understand those you should go back and read my previous blog post before continuing.
writelist([]). % Stopping condition.
writelist([ Head | Tail ]) :-
% Write out the head of the list.
write(' - '), write(Head), nl,
% Recursively write out the rest of the list.
writelist(Tail).
concat([], List, List). % Stopping condition - The 2nd list and the result should be one and the same
% Add the head of the first list onto the result
concat([ Head | Tail ], List2, [ Head | Result ]) :-
% Recursively add the next item in the first list to the result
concat(Tail, List2, Result).
countlist([ LastItem | [] ], LastItem). % Stopping condition - Don't continue if we reach the end of the list.
countlist([ Head | Rest ], Result) :-
% Recursively count the rest of the list
countlist(Rest, RecursiveResult),
% Add the head to the result from recursively counting the rest of the list.
Result is RecursiveResult + Head.
listlength([ _ | [] ], 1).
listlength([ _ | Tail ], Result) :-
listlength(Tail, RecursiveResult),
Result is RecursiveResult + 1.
All of the above use a similar loop structure. They recursively sort out the tail, and then deal with the tail when we coming back out again. Here are some examples of the above rules (virtual cookie for whoever works out what all the things in each of the lists below have in common). Don't forget to ask questions in the comments if you don't understand something.
?- writelist([ cat, dog, parrot, elephant, whale]).
- cat
- dog
- parrot
- elephant
- whale
true
?- countlist([ 4, 7, 2, 18, 48, 364 ], Result).
Result = 443 .
?- listlength([ alpha_centauri, sirius, barnards_star, epsilon_eridani, tau_ceti], Length).
Length = 5 .
?- concat([elysium_mons, arsia_mons, aeolis_mons], [skadi_mons, maat_mons], Result).
Result = [elysium_mons, arsia_mons, aeolis_mons, skadi_mons, maat_mons].
Find all the things!
The other thing we looked at this week was the findall/3
predicate. In a nutshell, findall
will find everything that matches the given criteria and put them in a list. Here's a simple example:
loves(cat, milk).
loves(dog, bone).
loves(mouse, cheese).
loves(mouse, seeds).
loves(cat, cheese). % Yes, my cat does love cheese...!
loves(parrot, seeds).
?- findall(Who, loves(Who, cheese), Result), writelist(Result).
- mouse
- cat
true.
In the above example, we ask Prolog to find us everyone who loves cheese, and then print out the result. The second argument is the criteria we want Prolog to use when searching, the first argument is the variable from the criteria that we want to store, and the third argument is the list we want populating.
As an exercise, try to work out how to ask Prolog who likes seeds, given the above dataset. If you find that easy, try asking Prolog to list everyone who likes both seeds and cheese. The answers will be in the comments.
findall
can be used to search many different datasets. Here's another more complicated example:
isa(polly, parrot).
isa(parrot, bird).
isa(bird, living_thing).
hasa(parrot, wings(2)).
hasa(polly, perch).
hasa(polly, colour(green)).
?- findall(green_birds(Who), hasa(Who, colour(green)), Result).
Result = [green_birds(polly)] .
That concludes this blog post on the 7th Prolog lab. If you are interested, here are some links to the source code for the examples found in this post, along with a few extras.