我遇到了lisp宏的问题。我想创建一个宏 根据数组生成一个开关盒。
以下是生成switch-case的代码:
(defun split-elem(val)
`(,(car val) ',(cdr val)))
(defmacro generate-switch-case (var opts)
`(case ,var
,(mapcar #'split-elem opts)))
我可以使用这样的代码:
(generate-switch-case onevar ((a . A) (b . B)))
但是当我尝试做这样的事情时:
(defparameter *operators* '((+ . OPERATOR-PLUS)
(- . OPERATOR-MINUS)
(/ . OPERATOR-DIVIDE)
(= . OPERATOR-EQUAL)
(* . OPERATOR-MULT)))
(defmacro tokenize (data ops)
(let ((sym (string->list data)))
(mapcan (lambda (x) (generate-switch-case x ops)) sym)))
(tokenize data *operators*)
我收到了这个错误:*** - MAPCAR: A proper list must not end with OPS
,但我不明白为什么。
当我打印ops
的类型时,我得到SYMBOL
我期待CONS
,它是否相关?
另外,对于我的函数tokenize
,lambda评估了多少次(或扩展了宏?)
感谢。
答案 0 :(得分:1)
这没有任何意义。您尝试使用功能足够的宏。
你想要的是这样的:
(defun tokenize (data ops)
(mapcar (lambda (d)
(cdr (assoc d ops)))
(string->list data)))
CASE是一个需要大量固定子句的宏。它不接受在运行时计算的子句。如果列表数据应驱动计算,则使用ASSOC等函数。
GENERATE-SWITCH-CASE也是一个奇怪的名字,因为宏是一个转换案例。
GENERATE-SWITCH-CASE也确实将列表作为第二个参数。但在TOKENIZE中,你用符号OPS称呼它。请记住,使用Lisp源代码计算宏。
接下来,还没有涉及ARRAY。 Lisp有数组,但在你的例子中没有数组。
典型建议:
如果你想写一个MACRO,请再想一想。把它写成一个函数。
如果你还想写一个宏,请转到1.