Reputation: 11
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
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
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