terrapin
terrapin

Reputation: 11

lisp illegal function call

I'm just starting to learn Common Lisp, and I was giving the first Project Euler problem a try (summing all numbers below x that are divisible by 3 or 5). I tried to define a macro to generalize the process to numbers that are divisible by a given list of factors, and ran into trouble: when I run the macro it says that there was an illegal function call with setf, and warned that sum is undefined. Other people have posted this question before and had problems with parentheses, but I made an example of what I hoped the macro would expand into, and that function works fine, and the parentheses are exactly in the same places. Here's the code for the example function (which works fine) and the macro (which throws the errors):

;;; Example function for macro 
(defun count-multiples-example (limit)
  (let ((sum 0))
    (dotimes (n (1+ limit) sum)
      (dolist (each '(3 5))
        (when (= 0 (mod n each))
          (setf sum (+ n sum)) 
          (return))))))

;;; Macro for arbitrary numbers to divide by (eventually)
(defmacro count-arbitrary (limit &rest divisors)
  (let ((sum 0))
    `(dotimes (n (1+ ,limit) ,sum)
      (dolist (each ,divisors)
        (when (= 0 (mod n each)) 
          (setf sum (+ n ,sum)) 
          (return))))))

I'm using SBCL with lispstick. Thanks!

Upvotes: 1

Views: 583

Answers (2)

Rainer Joswig
Rainer Joswig

Reputation: 139241

CL-USER 28 > (defmacro count-arbitrary (limit &rest divisors)
               (let ((sum 0))
                 `(dotimes (n (1+ ,limit) ,sum)
                    (dolist (each ,divisors)
                      (when (= 0 (mod n each)) 
                        (setf sum (+ n ,sum)) 
                        (return))))))
COUNT-ARBITRARY

Let's look at the expansion:

CL-USER 29 > (pprint (macroexpand-1 '(count-arbitrary 30 3 5)))

(DOTIMES (N (1+ 30) 0)
  (DOLIST (EACH (3 5))
    (WHEN (= 0 (MOD N EACH))
      (SETF SUM (+ N 0)) (RETURN))))

You can see that the LET for the sum variable is missing, (3 5) lacks a quote (it is thus an illegal function call) and both comma before sum are wrong.

Generally the macro makes little sense, since you can provide the numbers as an additional parameter to the function:

(defun count-multiples-example (limit divisors &aux (sum 0))
  (dotimes (n (1+ limit) sum)
    (dolist (each divisors)
      (when (= 0 (mod n each))
        (incf sum n)
        (return)))))

or this:

CL-USER 35 > (defun count-multiples-example (limit &rest divisors &aux (sum 0))
               (dotimes (n (1+ limit) sum)
                 (dolist (each divisors)
                   (when (zerop (mod n each))
                     (incf sum n)
                     (return)))))
COUNT-MULTIPLES-EXAMPLE

CL-USER 36 > (count-multiples-example 30 3 5)
225

Upvotes: 6

Arndt Jonasson
Arndt Jonasson

Reputation: 854

If I move the small dots around a little, this works for me:

(defmacro count-arbitrary (limit &rest divisors)
  `(let ((sum 0))
     (dotimes (n (1+ ,limit) sum)
       (dolist (each ',divisors)
         (when (= 0 (mod n each)) 
           (setf sum (+ n sum)) 
           (return))))))

Upvotes: 1

Related Questions