我刚刚开始学习Common Lisp,我尝试了第一个Project Euler问题(将x下面的所有数字相加,可以被3或5整除).我试图定义一个宏来将进程概括为可被给定的因子列表整除的数字,并遇到麻烦:当我运行宏时,它说有一个非法的函数调用setf,并警告它sum是未定义的.其他人之前已经发布过这个问题并且在括号中出现问题,但是我做了一个我希望宏会扩展到的例子,并且该函数工作正常,并且括号完全在同一个地方.这是示例函数(工作正常)和宏(抛出错误)的代码:
;;; 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))))))
Run Code Online (Sandbox Code Playgroud)
我正在使用带有lispstick的SBCL.谢谢!
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
Run Code Online (Sandbox Code Playgroud)
让我们来看看扩展:
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))))
Run Code Online (Sandbox Code Playgroud)
你可以看到,LET该sum变量缺失,(3 5)缺乏报价(因此有一个非法函数调用)和两个逗号之前sum都是错误的.
通常,宏没有意义,因为您可以将数字作为函数的附加参数提供:
(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)))))
Run Code Online (Sandbox Code Playgroud)
或这个:
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
Run Code Online (Sandbox Code Playgroud)