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:

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

  2. 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!

  1. 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 .
  2. 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.
  3. 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 .
  4. 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.
  5. 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 .
  6. 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 or fls) 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 pushes tru if the second number from the top is less than or equal to the top number, otherwise it pushes fls.
  • An if(P1,P2) command pops a boolean value off the stack and executes either P1 if the value is tru or P2 if the value is fls.

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.

  1. Define the predicate cmd/3, which describes the effect of a command on the stack. That is, the predicate cmd(C,S1,S2) means that executing command C with stack S1 produces stack S2.

    ?- 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 the prog/3 predicate below.

  2. Define the predicate prog/3, which describes the effect of a program on the stack. That is, the predicate prog(P,S1,S2) means that executing program P with stack S1 produces stack S2.

    ?- 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!"] .


Back to course home page