You should submit one file containing all your programs along with
documentation. Your programs will be run against our solutions and
results generated.
This may provide feedback to your work.
You can keep submitting, and the last one before the deadline will be
retained as your submission.
Questions on the try command can be directed to tommy@cs.ualberta.ca

Assignment Marks: There are a total of 65 marks possible on
this assignment. It is worth 8% of the total course grade.
Your programs must be readable and understandable as well as correct.
You should read the guidelines as given in programming
style and marking guideline.
You could lose up to 1/2 of the marks for bad style even if your
programs are correct.
(25/Sept: Changed "penalty for bad style", from 1/3 to 1/2, to be
consistent with other documentation, and TA's earlier comments.)

Restrictions:

You may only use the built-in Lisp functions and special forms shown
here

You may write one or more functions to solve any given problem
below.
In some cases it is desirable to decompose a problem into smaller ones.
However, if a problem has a straightforward single-function solution,
it's a bad idea to solve it in a complex way by decomposition.

#1 (5 marks) Write the Lisp function

(my-min L)

that returns the minimum value of the argument list-of-numbers L.
You may assume L is a list
with at least 1 element, which includes only numeric values.

>(my-min '(3 1 -5 2)-5

>(my-min '(1))1

#2 (10 mark) Write the Lisp function

(deep-member x y)

that returns T if
the first argument x appears
anywhere at any level in the
argument recursive-list y
and NIL
otherwise. This should test for lists being members of lists.
Both the argument x and the
list y may be NIL or lists containing NIL.
See the examples.
You cannot just call the built-in function member
(see restrictions above).(22/Sept: Corrected version appears above -- the first argument need not be
an atom.)
Examples:

that returns returns a list L1 formed by replacing, within the
recursive-list L, each
occurance of the atom x (at
any level) with the atom y.
(You may assume that L is a
recursive list, and that x
and y are each atoms.)

>(deep-subst 4 t '(truth is 4))(TRUTH IS T)

>(deep-subst4 nil '(4 is not (4)))(NIL IS not (NIL))

>(deep-subst 4 nil '(5 is not (22)))(5 is not (22))

#4 (5 marks) Write the Lisp function

(lookup atom pair-list)

that finds the "atom-entry"
within the list of pairs pair-list,
or NIL if atom does not match any of the
entries in pair-list .
You may assume that atom is
an atom and pair-list is a
list whose entries are each 2-element lists, whose FIRST element is always an atom.

>(lookup '5 '((t frog) (5 what) (nil 1)))(5 what)

>(lookup 5 '((1 this) (2 is) (3 a) (4 test)) )nil

#5 (15 marks) In general, a "conjunctive normal form"
(CNF) formula is a conjunction of clauses, where each clause
is a disjunction of literals, and each literal is either a
variable or its negation -- eg,

cnf1 = (-x1 v -x2 v +x3) & (+x2 v +x4 v -x5)

(We will later see that any boolean formula can be expressed as a CNF
formula.)
Given a set of variables, an assignment maps each variable to either
true (T) or false (NIL) --
e.g., the assignment

assgn1 = ( (x1 t) (x2 nil) (x3 t) (x4 nil) (x5 t ) )

maps x1 to T, x2 to NIL, x3 to T, x4 to NIL and x5 to T.
Given a specific assign assgn, the
CNF formula cnf evaluates to T if each of its clauses evaluates
to T (otherwise it evaluates NIL); and a clause evaluates to T if at least one of its literals
evaluates to T (otherwise it
evaluates to NIL), and a
positive literal +X1 is T if it is assigned to T in assgn; and a negative literal -X3 is T if it is assigned to NIL in assgn.Eg, using assgn1
, the first clause of cnf1,

(-x1 v -x2 v +x3)

evaluates to T as its 2nd literal, -x2, evaluates to T as assgn1 includes (x2 nil).However, cnf1's second
clause

(+x2 v +x4 v -x5)

evaluates to NIL as each of
its 3 literals evaluates to NIL.Hence, the conjunction of these clauses,cnf1,
evaluates to NIL.Write the Lisp function

(eval-cnf cnf assgn)

that evaluates the boolean CNF formula cnf formula, with respect to the
assignment assgn, returning T if assgn satisfies cnf, and NIL otherwise.
We encode a CNF as a list of clauses, where each clause is represented
as a list of literals, and each literal is a two element list of + or
-, followed by variable-name. ("+" means the variable is
true, "-" for false).
Hence we would write

(+x1 v -x2 v +x3) & (+x2 v +x4 v -x5)

as

( ((+ x1) (- x2) (+ x3)) ((+ x2) (+ x4) (- x5)) )

As shown above, we represent an assignment as a list of (variable value) pairs;
see assgn1 above.

Note the empty CNF list is true, and the empty clause (within the
list) is false.(23/Sept: Corrected version appears above -- the "true" and "false"
were backwards!
Also corrected the first example below, and added another example.)

#6 (20 mark)
Satisfiability -- finding a
satisfying assignment for a boolean formula -- is one of the core ideas
in computing science. It is used for verifying hardware, verifying
software, reasoning, planning, as well as other areas of artificial
intelligence. And of course, all NP-hard problems reduce to
satisfiability. We will later see it is the core procedure for
proving arbitrary conclusions from known information.

Write a LISP function

(sat cnf)

that returns a satisfying assignment
if the CNF formula cnf
is satisfiable, and otherwise return NIL.

This function should use the simple "generate and test"
approach: successively generate each possible assignments to the
variables, then test if that assignment satisfies the CNF formula --
ie, if it makes the CNF true (hint: think eval-cnf).
Given a list of the variables in the formula, there is a simple
recursive way to walk through the set of all assignments. You
need only generate one assignment -- ie, your function should terminate
on finding a satisfying assignment.