CSCI 330 Quiz 3: lisp III Sample Solutions

Question 1 [4 marks]

This question has two parts (part b is on the reverse side of the page).

Part a: functions returning multiple values

(i) Complete the implemention of the inverses function below.

(ii) Provide a short code segment illustrating the capture and use of both values.
; if x and y are both reals then inverses returns two values,
;    the first being x/y and the second being y/x
; otherwise inverses simply returns one value, nil
(defun inverses (x y)

; Sample solution

   ; (i) body of inverses
   (if (and (realp x) (realp y))
       (values (/ x y) (/ y x))
       nil))

; (ii) sample call to capture/use both returned values

   (multiple-value-bind (a b) (inverses 5 13)
      (format t "~A,~A~%" a b))

Part b: recursive variadic functions

Explain the problem with the function below and provide a simple fix that ensures it works as intended.
; return a count of the number of parameters
;    that are integers greater than 0
(defun numPosInts (&rest R)
   (cond
      ((null R) 0)
      ((or (not (integerp (car R))) (< (car R) 1)) (numPosInts (cdr R)))
      (t (+ 1 (numPosInts (cdr R))))))

Sample Solution

The problem lies with how the recursive call passes the elements of R as parameters.
If the original call was (numPosInts 5 6 7)
   then we would want the recursive call to handle everything after the 5, e.g.
        (numPosInts 6 7)
   but because the 6 and 7 are stored in R as '(6 7) what we actually call is
        (numPosInts '(6 7))
   so in the recursive call the first element is a list (6 7),
      which is treated as a non-integer so not counted,
   and there are no elements left to process

A fix would be to make the recursive call using apply, i.e.
   (apply 'numPosInts (cdr R))
so that the elements of R become individual parameters in the recursive call.

Question 2 basic macros [4 marks]

Provide an implementation for the useCompute macro below.
; the user provides a local variable name to use,
;    a computation whose result is to be stored in the local variable,
;    then zero or more statements using the local variable
(defmacro useComputed (name computation &rest body)
   ; ... to be completed ...
   )

; example: here the root of 10 should be computed and stored in a local
;    variable named tmp, and there are two subsequent statements using tmp
(defvar x 10)
(useComputed tmp (sqrt x)
   (format t "given ~A, the root is ~A~%" x tmp)
   (format t "and their product is ~A~%" (* x tmp)))

Sample solution

(defmacro useComputed (name computation &rest body)
   `(let ((,name ,computation)) ,@body))

The transformed code creates a let block with one local variable,
    using the name the programmer provided and initialized using the computation provided,
and then the body of the let block is the collection of statements the programmer provided,
    each of which is free to use the designated name (and can trust it holds the
    computed value)


Question 3 variadic recursive macros [4 marks]

Complete the variadic macro q3 below using a recursive approach (i.e. not simply applying reduce to a list of x, y, and the elements of R).

The macro is intended to transform its elements into a nested set of pairs:
(defmacro q3 (x y &rest R)
   ; ... to be completed ...
   )

Sample solution

(defmacro q3 (x y &rest R)
   (if (null R) `(list ,x ,y)
       `(q3 (list ,x ,y) ,@R)))

Effectively this is saying
   if I see (q3 x y) then the code to be generated is simply (list x y)
   otherwise
       the inner (list ,x ,y) builds a list of the current front two elements
       and the outer (q3 ,@R) embeds that inside a larger call
   thus
      (q3 a b) is simply (list a b)
      (q3 a b c) becomes (q3 (list a b) c)
          which in turn becomes (list (list a b) c)
      (q3 a b c d) becomes (q3 (list a b) c d)
          which in turn becomes (q3 (list (list a b) c) d)
          which then becomes (list (list (list a b) c) d)
      etc
   Each recursive layer effectively takes the list from the previous layer as its first element
      and the next value from R as the second, builds that into a new list layer,
      then recurses for the rest