我正在学习(普通)Lisp,作为练习,我想实现一个cond
宏“xond”,它可以改变这个愚蠢的例子:
(xond (= n 1) (setq x 2) (= n 2) (setq x 1))
进入 if-else 链:
(if (= n 1) (setq x 2) (if (= n 2) (setq x 1)))
目前,我有这个宏:
(defmacro xond (&rest x) (if x (list 'progn (list 'if (pop x) (pop x)))))
只是扩展前两项x
:
(macroexpand '(xond (= x 1) (setq y 2)))
生产
(PROGN (IF (= X 1) (SETQ Y 2))) ;
现在我想处理 中的所有项目x
,所以我添加了一个loop
来生成一个 if-serie(迈向 if-else-version 的一步):
(defmacro xond (&rest x)
(loop (if x
(list 'progn (list 'if (pop x) (pop x)))
(return t))))
但随后宏似乎停止工作:
(macroexpand '(xond (= x 1) (setq y 2)))
T ;
我在这里缺少什么?
版
verdammelt 的回答让我走上了正确的道路,而 coredump 让我改变了我的迭代方法。
现在我将实现(xond test1 exp1 test2 exp2)
为:
(block nil
test1 (return exp1)
test2 (return exp2)
)
这可以通过迭代来完成。
我正在为我的最小 Lisp 解释器写这个;我只实现了最基本的功能。
这是我写的。我la
用来累积输出的部分。
(defmacro xond (&rest x)
(let ((la '()))
(loop
(if x (push (list 'if (pop x) (list 'return (pop x))) la)
(progn (push 'nil la)
(push 'block la)
(return la)
)))))
和
(macroexpand '(xond (= x 1) (setq y 2) (= X 2) (setq y 1)))
结果:
(BLOCK NIL
(IF (= X 2) (RETURN (SETQ Y 1)))
(IF (= X 1) (RETURN (SETQ Y 2)))
) ;
第二版
添加标签block
并更改return
为return-from
,以避免与其他return
内部参数冲突。还更改push
为append
以与参数相同的顺序生成代码。
(defmacro xond (&rest x)
(let ((label (gensym)) (la '()) (condition nil) (expresion nil))
(setq la (append la (list 'block label)))
(loop
(if x
(setq la (append la (list
(list 'if (pop x) (list 'return-from label (pop x))))))
(return la)))))
所以
(macroexpand '(xond (= x 1) (setq y 2) (= X 2) (setq y 1)))
现在给
(BLOCK #:G3187 (IF (= X 1) (RETURN-FROM #:G3187 (SETQ Y 2))) (IF (= X 2) (RETURN-FROM #:G3187 (SETQ Y 1))))