homework.pdf

ECS140A-F16-07 October 27, 2016

ASSIGNMENT 5: LISP

Due: November 8, 2016

Overview

The purpose of this assignment is for you to gain some experience designing and implementing

LISP programs. This assignment explores only a few of the many interesting LISP features.

This assignment is broken into several parts. The first part is a fairly straightforward LISP

warmup. The second part continues the warmup. The third part involves writing your own ver-

sion of the standard LISP function every; the remaining parts will use your function. The fourth

part illustrates the standard technique of car-cdr recursion. The remaining parts involve writing

functions that rewrite LISP expressions. More specifically, these parts takes as input a LISP

expression and produces as output another, possibly different LISP expression. The modified

expression will be semantically equivalent to the original, but it will use if’s rather than cond’s.

N.B., you are restricted as to which LISP functions you are allowed to use for various parts of this

assignment. See “Notes” for details.

1

ECS140A-F16-07 October 27, 2016

Part 1: The Cross1 Functions

Write the following three functions.

cross1-recursive (x y)

cross1-iterative (x y)

cross1-mapcar (x y)

If x or y is not a list, the function returns nil. Otherwise, each of these returns the list con-

sisting of pairs, one pair for each element of x; each pair contains as its first element an ele-

ment of x and as its second element the entire list y. The overall order of the list follows the

order from x. For example,

(cross1-recursive ’(1 2) ’(a b c))

returns

((1 (a b c)) (2 (a b c)))

cross1-recursive is to be written recursively, cross1-iterative is to be written iteratively

using either ‘go’ or ‘do’, and cross1-mapcar is to be written using ‘mapcar’.

Part 2: The Cross2 Functions

Write the following three functions.

cross2-recursive (x y)

cross2-iterative (x y)

cross2-mapcar (x y)

If x or y is not a list, the function returns nil. Otherwise, each of these returns the usual

2-dimensional cross-product of x and y. The overall order of the list follows the order from

x. For example,

(cross2-recursive ’(1 2) ’(a b c))

returns

((1 a) (1 b) (1 c) (2 a) (2 b) (2 c))

cross2-recursive is to be written recursively, cross2-iterative is to be written iteratively

using either ‘go’ or ‘do’, and cross1-mapcar is to be written using ‘mapcar’.

If you want, cross2-recursive can use cross1-recursive, cross1-iterative can use cross1-iterative,

and cross1-mapcar can use cross1-mapcar. However, doing so didn’t simplify my solutions.

Hint: for cross2-mapcar, use the “apply-append trick” (see text). In fact, there’s a nice one-line

solution that uses the apply-append trick and lambda expressions, but don’t fret about getting that

solution; get something working first.

2

ECS140A-F16-07 October 27, 2016

Part 3: The my-every Function

The LISP predicate every returns t or nil to indicate whether a given function holds for every ele-

ment in a list. Examples:

Expression Returns

(every #’integerp ’(2 4 8)) t

(every #’integerp ’(2 nope 8)) nil

Evaluation of every stops early, and returns nil, if it finds an element for which the given function

evaluates to nil.

Write your own version of every

my-every (fun q)

it behaves exactly as every using fun with argument list q. Assume that my-every is invoked

with only one argument list and that fun can be evaluated on one argument. You’ll need to

use funcall. Write my-every recursively. Use no iteration or mapping functions!

Just FYI, LISP’s every also allows functions with multiple arguments (just as mapcar does).

Examples:

Expression Returns

(every #’> ’(1 2 3) ’(0 0 2)) t

(every #’> ’(1 2 3) ’(0 4 2)) nil

That’s beyond the scope of my-every.

3

ECS140A-F16-07 October 27, 2016

Part 4: The flatp Function 1

A “flat” list is a list that contains no nested lists.

Write the function

flatp (x)

it returns t if x is flat and nil if x isn’t flat. Assume x is a list. Examples:

list flatp lenLFL (explained in Part 5)

() t 0

(()) nil 0

(1 2 3) t 3

(1 (2) 3) nil 1

(1 () 3) nil 0

((1 2 3 4)) nil 4

(1 2 (1 (2 3 4) 5 6 7 8)) nil 3

Hint: use my-every.

Part 5: The lenLFL Function

Write the function

lenLFL (x)

it returns:

• if x is flat, the length of x

• if x isn’t flat, the length of the longest flat list recursively within x.

That is, consider each element of x that is a list and recursively apply this definition.

See the examples in the table in Part 4.

Hint: use flatp; use car-cdr recursion to get inside nested lists. Use LISP’s max function, which

works with two or more integer arguments (but not on a list of integers).

1 The name flatp (and later the name legalcondp) follows the sometime LISP convention in which a “p” appended

to a name means predicate, as in a function that returns only t or nil. LISP is not consistent in its naming of predicates,

e.g., atom vs. listp. The Scheme dialect of LISP, though, is more consistent.

4

ECS140A-F16-07 October 27, 2016

Part 6: The legalcondp Function

This part considers what constitutes a “legal” cond. Our definition will be simpler than (and a

subset of) what LISP allows. A legal cond has the form

(cond a 1

a 2

... a N

)

• N must be ≥ 1. Terminology:

• arm — an a i

• Each arm a i

is a list with 1 or 2 elements. (LISP allows any non-empty list; we don’t.)

Terminology:

• conditional — a i ’s first element

• body — a i ’s second element

Thus, an arm consists of a conditional and, optionally, a body.

Write the function

legalcondp (x)

it returns t if all cond expressions that the s-expr x contains are legal; or it returns nil if x

contains any illegal cond expression.

Examples:

s-expr legalcondp reason

(cond) nil too few arms

(cond ()) nil arm has no conditional

(cond 3) nil arm isn’t a list

(cond (3)) t

(cond (3) (4 5) 6) nil last arm isn’t a list

(cond (3) (4 5) (6)) t

3 t no cond, so it’s fine

(alpha beta (gamma)) t

(foo bar (cond (3) (4 5) (6))) t

(foo (cond (3) (4 5) (6 (cond (3 2))))) t

(foo (cond (3) (4 5) (6 (cond ())))) nil nested cond missing conditional

Hint: use car-cdr recursion to get inside nested lists.

5

ECS140A-F16-07 October 27, 2016

Part 7: The Rewrite Function

Write the function

re write (x)

x is an s-expr. If x is not a “legal” cond, re write returns nil. Otherwise, re write returns an

expression in which each cond has been replaced by an equivalent sequence of nested if-

then.

Examples:

Expression Returns

(rewrite ’(* 44 2)) (* 44 2)

(rewrite ’(cond (3 4) (t 5))) (if 3 4 (if t 5))

(rewrite ’(cond ((= 3 3) 11) (t 15))) (if (= 3 3) 11 (if t 15))

(rewrite ’(cond ((= 3 4) 11) ((= 5 6) 12) (t 17))) (if (= 3 4) 11 (if (= 5 6) 12 (if t 17)))

(rewrite ’(list (cond ((= 8 8) ’y)) (cond ((= 8 7) ’no)))) (list (if (= 8 8) ’y) (if (= 8 7) ’no))

Note that each cond is rewritten to use only a sequence of nested if-then expressions. (They do

not use any if-then-else expressions; that’s for Part 9.)

Reminders about evaluating a cond:

• If no conditional is found true, cond returns nil.

• If an arm’s conditional evaluates to true and the arm’s body is empty, the arm returns the

value of the arm’s conditional. We’ll assume that the arm’s conditional has no side effects,

so that the same expression can be replicated as the “then” for the if (since an if requires a

“then” part).

• Recall that LISP allows a body of a cond’s arm to contain multiple s-expr. Each would be

executed and the value of the last s-expr is what’s returned. Having multiple s-expressions

is useful only if the non-last s-expressions have side effects. This HW, as noted previ-

ously, allows only one s-expression at most in each arm’s body.

Examples illustrating cond evaluation:

Expression Returns reason

(cond (3 4)) 4 3 is the condition, 4 is the return value

(cond ((= 3 4) 5)) nil guard not true, entire cond returns nil

(cond (3)) 3 no expression following conditional,

return conditional’s value

6

ECS140A-F16-07 October 27, 2016

Part 8: The Check Function

The function

check (x)

x is an s-expr. If x is not a “legal” cond, check returns nil. Otherwise, check returns a list of

three values. The second element is the result of evaluating expression x. The third element

is the result of evaluating the result of (rewrite x). The first element is t or nil corresponding

to whether or not the value of the second element is equal to that of the third element. Note

that if your rewrite function (and check function!) is correct, the first element of each list

that check returns will be "t". Assume that x contains no variables.

Part 9: The Rewrite-ite and Check-ite Functions

Write the function

re write-ite (x)

re write-ite (ite is short for “if-then-else”) is the same as re write, except it uses an if-then-

else in replacing the last part of a cond expression that

• has ≥ 2 arms

• and whose last arm’s conditional is exactly the symbol t.

These examples use the same arguments as in the table in Part 7; a † indicates that the value in

the Returns column differs from the corresponding value in that earlier table.

Expression Returns

(rewrite-ite ’(* 44 2)) (* 44 2)

(rewrite-ite ’(cond (3 4) (t 5))) † (if 3 4 5)

(rewrite-ite ’(cond ((= 3 3) 11) (t 15))) † (if (= 3 3) 11 15)

(rewrite-ite ’(cond ((= 3 4) 11) ((= 5 6) 12) (t 17))) † (if (= 3 4) 11 (if (= 5 6) 12 17))

(rewrite-te ’(list (cond ((= 8 8) ’y)) (cond ((= 8 7) ’no)))) (list (if (= 8 8) ’y) (if (= 8 7) ’no))

Also write the function

check-ite (x)

It’s the analogue of check for re write-ite.

7

ECS140A-F16-07 October 27, 2016

Notes

• The command to use Common LISP is “clisp -q”. clisp is available on CSIF systems.

• Some editors provide specific support to simplify editing LISP functions, for example, emacs’s

LISP mode, vi’s -l option, and nedit’s and jot’s parenthesis matching.

• Appendix A of LISPcraft summarizes LISP’s built-in functions. Each function is explained

briefly. You will find this a very useful reference as you write and debug your program.

• The test program "test.l" is provided in the "given" on the class webpage. It exercises the func-

tions that you write; hence, there is no test data. When executing within LISP within a part

directory, you need only type “(load "../test.l")”. This file defines the test functions

test-cross1, test-cross2, etc.

Each of these functions exercises the corresponding functions that you are to write. For exam-

ple, to test your cross1 functions simply type

(test-cross1)

In addition, the function test simply invokes each of the above test functions. These test func-

tions use additional helper functions. For example,

(test-cross1-recursive)

tests only cross1-recursive. See the test file for additional helper functions. Do not use any of

the test function names as a name of one of your functions. Note that each of these functions

returns “t” when complete, so there will be an extra line of output.

• We’re also providing a “batch mode” test script (“tester”), which you should find very helpful.

• “Correct” output will also be given. Your output must match the “correct” output. By “match”,

we mean match exactly character by character, including blanks, on each line; also, do not add

or omit any blank lines. (For this program, since LISP is doing all the output, that shouldn’t be

hard.)

Be sure to read and follow relevant comments in the test program test.l. In any case, it is up to you to verify the correctness of your output.

• You may define additional helper functions that your main functions use. Be sure, though, to

name the main functions as specified since the test program uses those names.

• Coding restrictions.

See the LISP functions webpage under HW5 on the webpage. As noted there, use only PURE

functions for all functions you write, except:

cross1-iterative, cross2-iterative: PURE, BASICITERATIVE, prog, return, setq

cross1-mapcar, cross2-mapcar: PURE, MAPPING

(OK, I’ll be explicit: Don’t even think about using INDEFINITES for my-every!)

Use no global variables.

8

ECS140A-F16-07 October 27, 2016

• To define your own initial LISP environment, place an init.lsp file in the directory in which you

execute clisp and run clisp via “clisp -q -i init.lsp”. For example, you will probably want to put

the command

(setq *print-case* :downcase)

in that file.

• When developing your program, you might find it easier to test your functions first interactively

before using the test program. You might also find trace feature (LISPcraft section 11.5) or

print functions (including the format function) useful in debugging your functions.

• Your code’s execution on CSIF must not be grossly inefficient: your code must complete all the

provided tests in at most 10 seconds (which is very generous). If your code is taking longer,

then you are likely doing much needless recomputing. Using “let” expressions will likely help

you solve that problem.

• Grading will be divided as follows.

Percentage Function(s)

10 Part 1: The Cross1 Functions

15 Part 2: The Cross2 Functions

10 Part 3: The my-every Function

10 Part 4: The flatp Function

15 Part 5: The lenLFL Function

15 Part 6: The legalcondp Function

15 Part 7: The Rewrite Function

5 Part 8: The Check Function

5 Part 9: The Rewrite-ite and Check-ite Functions

• A few points to help the novice LISP programmer:

• Watch your use of “(”, “)”, “"”, and “’”. Be sure to quote things that need to be quoted,

e.g., the name of the file in load.

• To see how LISP reads in your function, use pretty printing. For example, (pprint (sym-

bol-function ‘foo)) will print out the definition of function foo, using indentation to show

nesting. This is useful to locate logically incorrect nesting due to, e.g., wrong parenthesiz-

ing.

• If you cause an error, Common LISP places you into a mode in which debugging can be

performed (LISPcraft section 11.2). To exit any lev el, except the top level, type “:q”. To

exit the top level, type “ˆD”. See the class handout for an example.

• See the webpage for exact details of what to turn in, the provided source files, etc. As usual,

you must develop this program in “part order”: No credit will be given for one part if the previ-

ous part is not entirely working.

9