CSCI 485 Lab 4

Notes and Corrections:

(1) In expandOpApplier.cl and the combined script, the test variable (lam) is incorrectly initialized with '(lambda-closure nil nil nil (L) L), and will cause script errors.
Try using this instead: (defvar lam (lambda (L) L))

(2) Note that the macro, as specified, can only work on lists of operators that are statically passed to the macro - you can't put the list in a variable and run the macro on the variable. (Either the macro needs to know the list structure at load time, or it needs to expand to code that produces and evaluates the desired list, which isn't the format the specs requested.)

Assume the lists will be statically provided. :-)

I've got a macro to produce code for the general case, where the expanded code is what in turn produces (and then evaluates) the desired expression (op1 (op2 ... val)), but the actual expanded code is significantly more involved than just the (op1 (op2 ... val)). (I'll post it with the rest of the assign4 solution.)

Assignment 4 is spread over three lab sessions, and involves implementing a variety of lisp scripts dealing with higher order functions, closures, macros, and self modifying code.

In the first of the three lab sessions we will focus on lisp higher order functions and closures, the second lab session will focus on macros, and the third lab session will focus on modifying functions.

Helpful code examples from the csci 330 lisp notes might include:

It might also be helpful to understand list implementations and their implications for operation side effects.

Skeleton code and sample runs

The table below shows the readne, plus the skeleton code and sample output provided in the repository for all five scripts.


  CSCI 485 Assignment 4 README
  ----------------------------

This assignment consists of creating a variety of different
lisp scripts dealing with macros, higher order functions,
closures, and self-modifying code.

The scripts, and their general subject matter, are:
   applyOpsFunction.cl - higher order functions
      The objective is to create a function that
      applies a list of functions to a given value, e.g.
         (applyOps '(car car last) '(1 (2 3 4)))
      returns the result of (car (car (last '(1 (2 3 4)))))

   applyOpsMacro.cl - macros
      The objective is to create a macro that expands to apply
      a list of functions to a given value, e.g.
         (applyOpsMac (car car last) '(1 2 3))
      would expand to
         (car (car (last '(1 2 3))))

   buildOpApplier.cl - closure
      The objective is to create a function that takes a list
      of functions as a parameter, and builds and returns the
      lambda expression for a function to apply that sequence
      of functions to a value, e.g.
          (buildOpApplier '(cdr car last))
      would build and return the following
          (lambda (L) (cdr (car (last L))))
      If this lambda expression were then evaluated, e.g. with eval,
      it would produce a valid function of the form
          (lambda-closure () () () (L) (cdr (car (last L))))

   expandOpApplier.cl - self modifying code
      The objective is to create a function that takes two parameters,
      a lambda closure of the form shown above and another function,
      and modify the lambda closure to apply the second function to
      its own result immediately before returning

      For example, if we stored the lambda-closure above in variable f,
      then the call (expandOpApplier f 'length) would actually modify f's
       lambda closure to become
          (lambda-closure () () () (L) (length (cdr (car (last L)))))

   combineBuilderAndExpander.cl - extra testing
      There is no new functionality to be developed in this script,
      just copying the buildOpApplier and expandOpApplier functions
      from the two previous scripts and showing that they produce
      compatible functions.


For each script, there is:

(1) a skeleton .cl file that includes the profile for the function or macro,
    comments describing the required function or macro behaviour,
    and several sample calls to test the function or macro

(2) a corresponding .txt file that shows output from a valid run of the script


#! /usr/bin/gcl -f ; given a list of unary operators, (op1 op2 ... opN) and a value, ; apply the operators in the following sequence and return the result: ; (op1 (op2 (... (opN val)...)) ; e.g. (applyOps '(cdr car last) L) ; would generate (cdr (car (last L))) (defun applyOps (opsList val) ; ... to be done ... ) (format t "~%evaluating (applyOps (car) (1 2 3))~%") (format t " expected result ~A~%" (car '(1 2 3))) (format t " result ~A~%" (applyOps '(car) '(1 2 3))) (format t "~%evaluating (applyOps (- car) (1 2 3))~%") (format t " expected result ~A~%" (- (car '(1 2 3)))) (format t " result ~A~%" (applyOps '(- car) '(1 2 3))) (format t "~%evaluating (applyOps (car car last) (1 (2 3 4)))~%") (format t " expected result ~A~%" (car (car (last '(1 (2 3 4)))))) (format t " result ~A~%" (applyOps '(car car last) '(1 (2 3 4))))
evaluating (applyOps (car) (1 2 3)) expected result 1 result 1 evaluating (applyOps (- car) (1 2 3)) expected result -1 result -1 evaluating (applyOps (car car last) (1 (2 3 4))) expected result 2 result 2
#! /usr/bin/gcl -f ; given a list of unary operators, (op1 op2 ... opN) and a value, ; generate an expression of the form ; (op1 (op2 (... (opN val)...)) ; e.g. (applyOpsMac '(cdr car last) L) ; would generate (cdr (car (last L))) (defmacro applyOpsMac (opsList val) ; ... to be done ... ) (format t "~%expand (applyOpsMac (car) (1 2 3))~%~A~%" (macroexpand-1 '(applyOpsMac (car) (1 2 3)))) (format t " expected result ~A~%" (car '(1 2 3))) (format t " result ~A~%" (applyOpsMac (car) '(1 2 3))) (format t "~%expand (applyOpsMac (- car) (1 2 3))~%~A~%" (macroexpand-1 '(applyOpsMac (- car) (1 2 3)))) (format t " expected result ~A~%" (- (car '(1 2 3)))) (format t " result ~A~%" (applyOpsMac (- car) '(1 2 3))) (format t "~%expand (applyOpsMac (car car last) (1 (2 3 4)))~%~A~%" (macroexpand-1 '(applyOpsMac (car car last) (1 (2 3 4))))) (format t " expected result ~A~%" (car (car (last '(1 (2 3 4)))))) (format t " result ~A~%" (applyOpsMac (car car last) '(1 (2 3 4))))
expand (applyOpsMac (car) (1 2 3)) (CAR (1 2 3)) expected result 1 result 1 expand (applyOpsMac (- car) (1 2 3)) (- (APPLYOPSMAC (CAR) (1 2 3))) expected result -1 result -1 expand (applyOpsMac (car car last) (1 (2 3 4))) (CAR (APPLYOPSMAC (CAR LAST) (1 (2 3 4)))) expected result 2 result 2
#! /usr/bin/gcl -f ; given a list of unary operators, (op1 op2 ... opN) and a value, ; build and return a lambda expression whose body (and return value) ; has the form (op1 (op2 (... (opN val)...))) ; e.g. (buildOpApplier '(cdr car last) L) ; would generate (lambda (L) (cdr (car (last L)))) (defun buildOpApplier (opsList &optional (sofar nil)) ; ... to be done ... ) ; build the lambda expression, evaluate it, and store the resulting function in f1 (defvar f1 (eval (buildOpApplier nil))) (format t "~%(buildOpApplier nil) creates~%~A~%" f1) (format t " applying lambda to (1 2 3)~%") (format t " expected result ~A~%" '(1 2 3)) (format t " result ~A~%" (funcall f1 '(1 2 3))) (defvar f2 (eval (buildOpApplier '(car)))) (format t "~%(buildOpApplier (car)) creates~%~A~%" f2) (format t " applying lambda to (1 2 3)~%") (format t " expected result ~A~%" (car '(1 2 3))) (format t " result ~A~%" (funcall f2 '(1 2 3))) (defvar f3 (eval (buildOpApplier '(- car)))) (format t "~%(buildOpApplier (- car)) creates~%~A~%" f3) (format t " applying lambda to (1 2 3)~%") (format t " expected result ~A~%" (- (car '(1 2 3)))) (format t " result ~A~%" (funcall f3 '(1 2 3))) (defvar f4 (eval (buildOpApplier '(car car last)))) (format t "~%(buildOpApplier (car car last)) creates~%~A~%" f4) (format t " applying lambda to (1 (2 3 4))~%") (format t " expected result ~A~%" (car (car (last '(1 (2 3 4)))))) (format t " result ~A~%" (funcall f4 '(1 (2 3 4))))
(buildOpApplier nil) creates (LAMBDA-CLOSURE () () () (L) L) applying lambda to (1 2 3) expected result (1 2 3) result (1 2 3) (buildOpApplier (car)) creates (LAMBDA-CLOSURE () () () (L) (CAR L)) applying lambda to (1 2 3) expected result 1 result 1 (buildOpApplier (- car)) creates (LAMBDA-CLOSURE () () () (L) (- (CAR L))) applying lambda to (1 2 3) expected result -1 result -1 (buildOpApplier (car car last)) creates (LAMBDA-CLOSURE () () () (L) (CAR (CAR (LAST L)))) applying lambda to (1 (2 3 4)) expected result 2 result 2
#! /usr/bin/gcl -f ; given two parameters: ; a function of the form created by buildOpApplier, func, ; and another unary operator, op, ; modify the passed function, prepending the operator to ; its current return statement ; e.g. if the original function was ; (lambda-closure nil nil nil (L) L) ; and we expand with operator car then ; the function should be changed to ; (lambda-closure nil nil nil (L) (car L)) (defun expandOpApplier (func op) ; ... to be done ... (defvar lam '(lambda-closure nil nil nil (L) L)) (format t "~%original function ~A~%" lam) (expandOpApplier lam 'car) (format t "~%adding op car gives function~% ~A~%" lam) (format t " applying new lambda to (1 2 3)~%") (format t " expected result ~A~%" 1) (format t " result ~A~%" (funcall lam '(1 2 3))) (expandOpApplier lam 'cdr) (format t "~%adding op cdr gives function~% ~A~%" lam) (format t " applying new lambda to ((1 2 3) 4 5)~%") (format t " expected result, i.e. cdr of car of L, is ~A~%" (cdr (car '((1 2 3) 4 5)))) (format t " result ~A~%" (funcall lam '((1 2 3) 4 5))) (expandOpApplier lam 'length) (format t "~%adding op length gives function~% ~A~%" lam) (format t " applying new lambda to ((1 2 3) 4 5)~%") (format t " expected result, i.e. length of cdr of car of L, is ~A~%" (length (cdr (car '((1 2 3) 4 5))))) (format t " result ~A~%" (funcall lam '((1 2 3) 4 5)))
original function (LAMBDA-CLOSURE NIL NIL NIL (L) L) Error: not given valid function to modify adding op car gives function (LAMBDA-CLOSURE NIL NIL NIL (L) L) applying new lambda to (1 2 3) expected result 1 result (1 2 3) Error: not given valid function to modify adding op cdr gives function (LAMBDA-CLOSURE NIL NIL NIL (L) L) applying new lambda to ((1 2 3) 4 5) expected result, i.e. cdr of car of L, is (2 3) result ((1 2 3) 4 5) Error: not given valid function to modify adding op length gives function (LAMBDA-CLOSURE NIL NIL NIL (L) L) applying new lambda to ((1 2 3) 4 5) expected result, i.e. length of cdr of car of L, is 2 result ((1 2 3) 4 5)
#! /usr/bin/gcl -f ; *** ASSUMING buildOpApplier and expandOpApplier were included here ; *** they could be used together as shown below ; evaluate the lambda expression and store the constructed function in lambdaFunc (defvar lambdaFunc (eval (buildOpApplier nil))) (format t "~%(buildOpApplier nil) creates~%~A~%" lambdaFunc) (format t " applying lambda to (1 2 3)~%") (format t " expected result ~A~%" '(1 2 3)) (format t " result ~A~%" (funcall lambdaFunc '(1 2 3))) (expandOpApplier lambdaFunc 'car) (format t "~%adding op car gives function~% ~A~%" lambdaFunc) (format t " applying new lambda to (1 2 3)~%") (format t " expected result ~A~%" 1) (format t " result ~A~%" (funcall lambdaFunc '(1 2 3))) (expandOpApplier lambdaFunc 'cdr) (format t "~%adding op cdr gives function~% ~A~%" lambdaFunc) (format t " applying new lambda to ((1 2 3) 4 5)~%") (format t " expected result, i.e. cdr of car of L, is ~A~%" (cdr (car '((1 2 3) 4 5)))) (format t " result ~A~%" (funcall lambdaFunc '((1 2 3) 4 5))) (expandOpApplier lambdaFunc 'length) (format t "~%adding op length gives function~% ~A~%" lambdaFunc) (format t " applying new lambda to ((1 2 3) 4 5)~%") (format t " expected result, i.e. length of cdr of car of L, is ~A~%" (length (cdr (car '((1 2 3) 4 5))))) (format t " result ~A~%" (funcall lambdaFunc '((1 2 3) 4 5)))
(buildOpApplier nil) creates (LAMBDA-CLOSURE () () () (L) L) applying lambda to (1 2 3) expected result (1 2 3) result (1 2 3) adding op car gives function (LAMBDA-CLOSURE () () () (L) (CAR L)) applying new lambda to (1 2 3) expected result 1 result 1 adding op cdr gives function (LAMBDA-CLOSURE () () () (L) (CDR (CAR L))) applying new lambda to ((1 2 3) 4 5) expected result, i.e. cdr of car of L, is (2 3) result (2 3) adding op length gives function (LAMBDA-CLOSURE () () () (L) (LENGTH (CDR (CAR L)))) applying new lambda to ((1 2 3) 4 5) expected result, i.e. length of cdr of car of L, is 2 result 2