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.
% 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
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.

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.

Tag Cloud

3d 3d printing account algorithms android announcement architecture archives arduino artificial intelligence artix assembly async audio automation backups bash batch blender blog bookmarklet booting bug hunting c sharp c++ challenge chrome os cluster code codepen coding conundrums coding conundrums evolved command line compilers compiling compression containerisation css dailyprogrammer data analysis debugging demystification distributed computing dns docker documentation downtime electronics email embedded systems encryption es6 features ethics event experiment external first impressions freeside future game github github gist gitlab graphics hardware hardware meetup holiday holidays html html5 html5 canvas infrastructure interfaces internet interoperability io.js jabber jam javascript js bin labs learning library linux lora low level lua maintenance manjaro minetest network networking nibriboard node.js open source operating systems optimisation own your code pepperminty wiki performance phd photos php pixelbot portable privacy problem solving programming problems project projects prolog protocol protocols pseudo 3d python reddit redis reference release releases rendering resource review rust searching secrets security series list server software sorting source code control statistics storage svg systemquery talks technical terminal textures thoughts three thing game three.js tool tutorial tutorials twitter ubuntu university update updates upgrade version control virtual reality virtualisation visual web website windows windows 10 worldeditadditions xmpp xslt

Art by Mythdael