Homework #6
Due: Fri, Mar 12
How to Submit
If submitting individually: Just upload your submission to the assignment in Canvas.
If submitting as part of a team:
Assign yourself to a “Group” in Canvas. Select “People” from the menu, then select the “Groups” tab. Click “Join” on a group whose name starts with “HW6”. (Sorry this list is getting obscenely long… I really wish Canvas had a better solution for this!)
You must coordinate with your team members to all join the same group. When choosing a group, please use the smallest available group number.
Do not add yourself to a group unless you have already coordinated and everyone has agreed to be in a group together.
Upload a single file named
hw6.pl
for your group for this assignment in Canvas. Only one group member needs to upload the file. Your file must contain the names and ID numbers of each member of your group in a comment at the top of your file. This is to ensure that nobody changed the composition of your group.
Notes on grading:
Your file must compile without errors in GHCi. If some of your code does not work, comment it out. If your code does not compile, the TAs will evaluate it harshly!
Do not change the existing doctest comments in the template (the lines starting with
>>>
and the results underneath). However, you are free to add your own doctest comments in addition to the ones that are there. The TAs will grade using both doctest and manual inspection of your file. If needed, they may also load your file in GHCi to run additional tests.If you can’t solve a problem, you can get partial credit by describing in comments what you tried and where you got stuck. Often times, writing this out helps you get unstuck!
Description
Template: hw6template.pl
Part 1. It’s a bird-eat-bug world out there!
In this part, you will define several Prolog predicates that describe relationships among animals.
The provided template defines two predicates, animal/3
and diet/2
, that together define a small database of facts about animals. The animal/3
predicate defines the animals the database knows about together with their habitat and their biological class. The diet/2
predicate defines what each animal eats. There are also two derived predicates habitat/2
and class/2
that make accessing information in the animal/3
database slightly easier.
You should not change the predicates that are already defined in the template.
Below are several predicates that you must implement. I have provided some example queries and the unifiers (solutions) that should be produced for each, or false
if no unifier exists.
Note: It is not important what order the unifiers are produced in and it is OK if a unifier is produced more than once! However, for all queries, all valid unifiers should eventually be produced, no invalid unifiers should be produced, and the query should not cause an infinite loop.
Also, you should try to define each predicate as simply as possible!
Define a predicate
neighbor/2
that determines whether two animals live in the same habitat. Note that two animals of the same kind always live in the same habitat.?- neighbor(squirrel, Animal). Animal = cranefly ; Animal = scrubjay ; Animal = squirrel ; Animal = woodpecker . ?- neighbor(Animal, minnow). Animal = duck ; Animal = minnow ; Animal = waterstrider .
Define a predicate
related/2
that includes all pairs of animals that are in the same biological class but are not the same kind of animal.?- related(waterstrider, Animal). Animal = cranefly . ?- related(Animal, scrubjay). Animal = duck ; Animal = woodpecker . ?- related(squirrel, Animal). false.
Define a predicate
competitor/3
that includes two kinds of animals and the food they compete for. Two animals are competitors if they live in the same place and eat the same food.?- competitor(scrubjay, Animal, Food). Animal = scrubjay, Food = insects ; Animal = scrubjay, Food = seeds ; Animal = squirrel, Food = seeds ; Animal = woodpecker, Food = insects . ?- competitor(Animal, duck, Food), Animal \= duck. Animal = minnow, Food = algae ; Animal = minnow, Food = insects ; Animal = waterstrider, Food = insects . ?- competitor(Animal1, Animal2, seeds), Animal1 \= Animal2. Animal1 = scrubjay, Animal2 = squirrel ; Animal1 = squirrel, Animal2 = scrubjay .
Define a predicate
would_eat/2
that includes all pairs of animals where the first animal would eat the second animal (because the second animal is a kind of food it eats), if it could.?- would_eat(Predator, waterstrider). Predator = scrubjay ; Predator = duck ; Predator = minnow ; Predator = waterstrider ; Predator = woodpecker . ?- would_eat(woodpecker, Prey). Prey = cranefly ; Prey = waterstrider . ?- would_eat(Predator, squirrel). false.
Define a predicate
does_eat/2
that includes all pairs of animals where the first animal would eat the second, and both animals live in the same place, so it probably does.?- does_eat(Predator, cranefly). Predator = scrubjay ; Predator = woodpecker . ?- does_eat(duck, Prey). Prey = minnow ; Prey = waterstrider .
Define a predicate
cannibal/1
that includes all animals that might eat their own kind–eek!?- cannibal(Animal). Animal = waterstrider .
Part 2. Implementing a stack language
In this part, you will define two predicates to implement a simple stack-based language. The syntax of the language is given below.
num | ::= | (any number literal) | |
str | ::= | (any string literal) | |
bool | ::= | tru | fls |
boolean literals |
prog | ::= | cmd* | sequence of commands |
cmd | ::= | num | str | bool | push a literal onto the stack |
| | dup | swap |
stack manipulation | |
| | add | mul | lte |
arithmetic and comparison | |
| | if( prog, prog) |
conditional branching |
The language is very similar to the stack language we implemented in Haskell earlier in the term, in StackLang.hs. A program is a list of commands, and each command has an effect on implicit program stack:
- A literal number (e.g.
3
), string (e.g."Hi there!"
), or boolean (i.e.tru
orfls
) is a command that simply pushes the corresponding value onto the stack. - The
dup
command duplicates the top element on the stack. - The
swap
command swaps the order of the top two elements on the stack. - The
add
command pops two numbers off the stack and pushes their sum. - The
mul
command pops two numbers off the stack and pushes their product. - The
lte
command pops two numbers off the stack and pushestru
if the second number from the top is less than or equal to the top number, otherwise it pushesfls
. - An
if(P1,P2)
command pops a boolean value off the stack and executes eitherP1
if the value istru
orP2
if the value isfls
.
Note that you do not need to implement the syntax of this language, nor do you need to explicitly check whether a command, program, or stack is valid. The important thing is that your definitions are correct given well-formed inputs. Consider the behavior in other cases undefined.
You should use Prolog lists to represent the program (i.e. a list of commands) and the stack (i.e. a list of number, string, and boolean values).
Prolog has built-in predicates number/1
and string/1
that can be used to recognize numbers and strings. See the example expr.pl from class for an example of how to use these predicates.
Define the predicate
cmd/3
, which describes the effect of a command on the stack. That is, the predicatecmd(C,S1,S2)
means that executing commandC
with stackS1
produces stackS2
.?- cmd("hello", [4], S). S = ["hello", 4]. ?- cmd(4, S, [4,"goodbye"]). S = ["goodbye"]. ?- cmd(dup, [3,4,5], S). S = [3, 3, 4, 5]. ?- cmd(dup, S, [2,2,3]). S = [2,3]. ?- cmd(swap, [2,3,4], S). S = [3, 2, 4]. ?- cmd(swap, S, [2,3,4]). S = [3, 2, 4]. ?- cmd(add, [2,3,4], S). S = [5, 4]. ?- cmd(mul, [2,3,4], S). S = [6, 4]. ?- cmd(lte, [3,2,4], S). S = [tru, 4]. ?- cmd(lte, [3,5,tru], S). S = [fls, tru].
Note that I have not provided a test case for
if
yet since it depends on theprog/3
predicate below.Define the predicate
prog/3
, which describes the effect of a program on the stack. That is, the predicateprog(P,S1,S2)
means that executing programP
with stackS1
produces stackS2
.?- prog([3,4,add], [], S). S = [7] . ?- prog([3,4,add,6,lte,9], [], S). S = [9, fls] . ?- prog([3,dup,dup,mul,swap], [], S). S = [3, 9] . ?- prog([if(["foo"],[3]),4],[tru,5],S). S = [4, "foo", 5] . ?- prog([2,lte,if(["foo"],[3]),4],[3],S). S = [4, 3] .
There is a larger example program defined in the file. An example of how to run this program and the expected output is shown below.
?- example(P), prog(P, [], S).
P = [2, 3, 4, lte, if([5, 6, add], [fls]), 3, swap, 4, 5|...],
S = ["the answer is", 6, "whew!"] .