Joh*_*4th 2 lisp macros common-lisp
我试图通过创建一个简单的+=宏和iterate宏来练习在Common Lisp中创建宏.我已经设法+=轻松地创建宏,我在我的iterate宏中使用它,我有几个问题.当我尝试运行我的宏时,例如
(iterate i 1 5 1 (print (list 'one i)))
Run Code Online (Sandbox Code Playgroud)
(i控制变量在哪里,1是起始值,5是结束值,1是增量值).我收到SETQ: variable X has no value
(defmacro += (x y)
(list 'setf x '(+ x y)))
(defmacro iterate (control beginExp endExp incExp &rest body)
(let ( (end (gensym)) (inc (gensym)))
`(do ( (,control ,beginExp (+= ,control ,inc)) (,end ,endExp) (,inc ,incExp) )
( (> ,control ,end) T)
,@ body
)
)
)
Run Code Online (Sandbox Code Playgroud)
我已经尝试了多种不同的东西来修复它,,并且这个错误使我不确定问题是否与iterate或+=.从我能说的+=工作正常.
Rai*_*wig 10
检查+=扩展以查找错误
您需要检查扩展:
CL-USER 3 > (defmacro += (x y)
(list 'setf x '(+ x y)))
+=
CL-USER 4 > (macroexpand-1 '(+= a 1))
(SETF A (+ X Y))
T
Run Code Online (Sandbox Code Playgroud)
上面的宏扩展显示x和y使用,这是错误.我们需要在宏函数中评估它们:
CL-USER 5 > (defmacro += (x y)
(list 'setf x (list '+ x y)))
+=
CL-USER 6 > (macroexpand-1 '(+= a 1))
(SETF A (+ A 1))
T
Run Code Online (Sandbox Code Playgroud)
上面看起来更好.请注意顺便说一句.该宏已经存在于标准的Common Lisp中.它被称为incf.
另请注意,您不需要它,因为iterate代码中不需要副作用.我们可以在+不设置任何变量的情况下使用该函数.
样式
您可能希望稍微调整一下Lisp样式:
GENSYM 采用参数字符串 - >提高生成代码的可读性&body而不是&rest- >改进宏表单的自动缩进使用iteratedo不需要+=宏来更新迭代变量,因为do更新变量本身 - >不需要副作用,我们只需要计算下一个值应用于您的代码,它现在看起来像这样:
(defmacro iterate (variable start end step &body body)
"Iterates VARIABLE from START to END by STEP.
For each step the BODY gets executed."
(let ((end-variable (gensym "END"))
(step-variable (gensym "STEP")))
`(do ((,variable ,start (+ ,variable ,step-variable))
(,end-variable ,end)
(,step-variable ,step))
((> ,variable ,end-variable) t)
,@body)))
Run Code Online (Sandbox Code Playgroud)
在Lisp中,第一部分 - 变量,开始,结束,步骤 - 通常写在列表中.例如,参见DOTIMES.这使得例如可以使其成为step可选项并为其赋予默认值:
(defmacro iterate ((variable start end &optional (step 1)) &body body)
"Iterates VARIABLE from START to END by STEP.
For each step the BODY gets executed."
(let ((end-variable (gensym "END"))
(step-variable (gensym "STEP")))
`(do ((,variable ,start (+ ,variable ,step-variable))
(,end-variable ,end)
(,step-variable ,step))
((> ,variable ,end-variable) t)
,@body)))
Run Code Online (Sandbox Code Playgroud)
让我们看看扩展,格式化以便于阅读.我们使用的函数macroexpand-1只进行一次宏扩展 - 而不是宏扩展生成的代码.
CL-USER 10 > (macroexpand-1 '(iterate (i 1 10 2)
(print i)
(print (* i 2))))
(DO ((I 1 (+ I #:STEP2864))
(#:END2863 10)
(#:STEP2864 2))
((> I #:END2863) T)
(PRINT I)
(PRINT (* I 2)))
T
Run Code Online (Sandbox Code Playgroud)
您可以看到创建的符号gensym也可以通过其名称识别.
我们也可以让Lisp格式化生成的代码,使用函数pprint并给出正确的余量.
CL-USER 18 > (let ((*print-right-margin* 40))
(pprint
(macroexpand-1
'(iterate (i 1 10 2)
(print i)
(print (* i 2))))))
(DO ((I 1 (+ I #:STEP2905))
(#:END2904 10)
(#:STEP2905 2))
((> I #:END2904) T)
(PRINT I)
(PRINT (* I 2)))
Run Code Online (Sandbox Code Playgroud)