| |
Computer Science 60
Principles of Computer Science
Spring 2010
Assignment 8: Prolog - yes! [100 points]
Due Sunday, March 28 by 11:59 PM
Submitting... For this assignment, you'll submit the
file: movies.pl or movies.pro. In order to write movies.pl,
you'll need the helper file movies_db.pl. Starter versions
of these files with examples and hints are available here. Be sure to
rename these files to end in .pl or .pro, not .txt
Again, be sure to rename these .txt files so that their extension suffix is .pl!
Formatting and commenting...
Please use the standard commenting
convention of your name, file name, and date at the top of each file.
In addition, be sure to include a comment for each function
that you implement. Examples appear through the starter files(s).
Part 0: Getting started... [0 points]
First, you will want to be sure you can run prolog -- either from
your computer or from the CS lab machines. You can also run prolog
from the LAC lab, but you will have to install it to your
own space on the H: drive.
Instructions from your own machine (or at the LAC):
- You can obtain a version named SWI prolog
from for either Windows or
Mac OS X. The Mac version requires you to have X11 (for the graphical interface) installed (if you have a
recent enough Mac, you probably do). The location of the program after it installs on a Mac is /opt/local/bin/swipl.
On the Mac, you'll need to run it from the command line (similar to the instructions below). On a PC,
you'll have a window that opens for the Prolog "command line" and others for editing your Prolog source files.
- Warning: the SWI prolog versions noted above use true and false instead of yes
and no in repsonding to queries. This decreases Prolog's joke-making ability by several orders of magnitude!
If you're on the Lab Macs You will need to open a terminal window and change directory to
the location of your simpsons.pl and movies.pl and movies_db.pl files -
you can download these from above.
To get a command-line prompt at which you can run Prolog:
- First, open a terminal window - there should be a console-like icon in the dock. Or, use
the searchlight finder (upper right) and seek Terminal.
- At the terminal's prompt, type cd Desktop, which should put you in the directory that is
your desktop.
- Download movies.txt to your desktop and change its name to
movies.pl.
Then, open the movies.pl file in a text editor. If you're not sure which one
to use, you might try Smultron, which is available on the lab Macs via clicking on the
background, heading to the "Go" menu, and then clicking through "Applications" - "Additional
Applications" - "Text" - "Smultron"
- Any text editor is OK, however. Smultron is available online for free.
- Go back to your terminal window and, when in the directory containing
movies.pl, type
swipl at the prompt. This will start prolog. If you're on your own Mac, then
you may have to type /opt/local/bin/swipl, providing the full pathname so that your
machine can find swipl.
- You can make sure it's working by entering Answer.
- You can load the movies.pl file by entering [movies]. (don't include the extension).
- You're set! You should be able to change movies.pl in your text editor,
reload it as above, and test the results...
- Good luck!
Part 1: The movie-database predicates...
[ 100 points, pair or individual ]
This problem asks you to write 10 prolog predicates (worth +10 points each)
whose descriptions are in the movies.pl file. We list them
here for reference:
- directedBandits
- directedByBay
- actressAfter1970
- player
- bornInLondon
- playerAndDirector
- playedAndDirected
- playedMultiple
- playedInComedy
- playedNotDirected
Be sure that you have the movies_db.pl file in the same
directory as your movies.pl file. The line at the top of movies.pl:
:- ensure_loaded('movies_db.pl').
seeks to load the movie database facts from movies_db.pl.
A prolog aside ...
Although it seems that Prolog offers an interpreter and a file interface - similar
to Scheme, keep in mind that Prolog is a bit different: you
must define everything in a file. Prolog's command line can only be
used to make queries based on the facts and rules loaded in from a file.
Therefore, the following will NOT work:
?- [movies]. <-- Loads in the file movies.pl
% movies compiled 0.42 sec, 4242 bytes
Yes
?- actor(zach_braff).
This is attempting to define a new fact inside the prolog environment,
and prolog will express its unhappiness.
Part 2: Extra credit! Lists in Prolog
[ up to +10 points, pair or individual ]
Here, you have the chance to try writing two Prolog predicates that express
relationships about lists. Please submit one or both of these
rules in one file named lists.pl. This link has a
starting point with placeholders. As before, you'll need to change the extension from
.txt to .pl.
You may create and use any helper rules that you like. In particular,
some of the rules that we saw in class may be helpful here! For example,
we wrote the member predicate:
member( X, [X|_] ).
member( X, [_|R] ) :- member( X, R ).
This is built in to some versions of prolog, but not others, so you may
want to include it!
Two extra credit predicates to try:
- removeOne(E, List, NewList) should be true if and only if E is an element of
list List one or more times, and the list NewList should be identical to
List except that
the element E is removed once.
Here are three example queries and their resulting Prolog interactions:
removeOne( a, [a,b,c,a], R ).
R = [b, c, a] ;
R = [a, b, c] ;
No
removeOne( d, [a,b,c,a], R ).
No
removeOne( E, [a,b,c,a], R ).
E = a,
R = [b, c, a] ;
E = b,
R = [a, c, a] ;
E = c,
R = [a, b, a] ;
E = a,
R = [a, b, c] ;
No
For full credit, your removeOne predicate must be able to work when either the first
or last argument, or both, are variables, as demonstrated above. You should assume that
the middle argument is bound to a list value (that is, it's not a variable).
Keep in mind the requirement that E be a member of
List!
The removeOne predicate requires no more than two rules. If you're writing
more than three (or even two) ~ take a step back and reconsider! In particular, remember that
in Prolog, negation is expressed through failure of its search. Thus, you don't
want a base case involving the empty list as the second argument!
After all, the rules should simply fail when
passed an empty list (directly or through recursion); that is, there should be no such rule at all.
- One-dimensional pattern matching is an important task in
many applications such as cryptography and DNA sequencing. For this problem we encode
a 1d pattern as a list of items (named Pattern) and we'll search for that
pattern within another list called the Target.
Thus, for this problem write the rule find(Pattern, Target,
Index) which takes two lists Pattern and Target
and a non-negative integer Index as input. This
find rule should be true if and only if Pattern occurs inside
list Target beginning at position Index.
The find predicate should not be true in the
case that Pattern is the empty list -- so your base case will
need to be bigger than the empty list!
For example, here is a Prolog query and response:
find([1, 2], [1, 2, 1, 2, 1], 0). % user query
More? % this prompt is prolog-dependent
Yes % the user hit return (not a semicolon)
The extra More? can be annoying when trying to test to see if
a fully-instantiated query succeeds or not. By printing that More?
Prolog is saying that it did succeed at the query -- it's
then asking if you'd like it to try to succeed again through a different
line of reasoning. You're probably not worried about that,
so you can simply hit return.
Nonessential aside on prolog internals
If you'd like an immediate Yes-or-No answer, you can cut off Prolog's future
searching with the cut operator, the exclamation point: !. Thus,
find([1, 2], [1, 2, 1, 2, 1], 0), !. % user query ~ one search only!
Yes % prolog succeeded
Using ! is not required, but it's there if you'd like to.
Alternatively, you can tell Prolog to stop being ridiculous
via the line
set_prolog_flag( prompt_alternatives_on, groundness ).
and it will stop the extra-prompting (unless there are variables present).
End of nonessential aside
Here are two more examples:
setof( N, find( [1,2], [1,2,1,2,1], N ), Answer ).
Answer = [0, 2]
and
find([], [1, 2, 1, 2, 1], N).
No.
Remember that find should not succeed for the empty Pattern.
For full credit, your find predicate should work when either or
both of Pattern and Index are Variables. We will only
test your code when Target is bound to a value (it's a non-variiable).
find( Pattern, [a,c,a,c], N ).
Pattern = [a],
N = 0 ;
Pattern = [a, c],
N = 0 ;
Pattern = [a, c, a],
N = 0 ;
Pattern = [a, c, a, c],
N = 0 ;
Pattern = [c],
N = 1 ;
Pattern = [c, a],
N = 1 ;
Pattern = [c, a, c],
N = 1 ;
Pattern = [a],
N = 2 ;
Pattern = [a, c],
N = 2 ;
Pattern = [c],
N = 3 ;
No
You can coax setof to give you all of these at once, if you'd like:
setof( Pair, P^N^(find( P, [a,c,a,c], N ), Pair=[N,P]), Answer ).
Answer = [[0, [a]], [0, [a, c]], [0, [a, c, a]], [0, [a, c, a, c]],
[1, [c]], [1, [c, a]], [1, [c, a, c]], [2, [a]],
[2, [a, c]], [3, [c]]]
The caret ^ acts as a "there exists" operator, i.e., the set of
Pair values such that there exists a P and an
N that satisfies the expression
(find( P, [a,c,a,c], N ), Pair=[N,P])
Yes - crazy, but true.
The find predicate requires no more than three rules. (You might re-design
things if your code is extending much beyond that... .)
|