Macros 如何构造此lisp宏?

Macros 如何构造此lisp宏?,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,我正在学习lisp的l99,学习lisp 这是来自,我希望应用宏只是为了练习,用宏编写所有的((x)(x(计算布尔左绑定)(计算布尔右绑定))s (defun evaluate-boolean (expression bindings) "Evaluates the boolean expression. Returns t or nil expression := variable | constant | '(' operator e

我正在学习lisp的l99,学习lisp

这是来自,我希望应用宏只是为了练习,用宏编写所有的
((x)(x(计算布尔左绑定)(计算布尔右绑定))
s

(defun evaluate-boolean (expression bindings)
  "Evaluates the boolean expression. Returns t or nil

expression := variable 
            | constant 
            | '(' operator expression expression ')' 
            | '(' not expression ')'
            .
constant := 'true' | 'fail' .
variable := symbol .
operator := 'and' | 'or' | 'nand' | 'nor' | 'xor' | 'impl' | 'equ' .

bindings is a list of pairs (variable . constant)
"
  (cond ((eq expression 'true) t)
        ((eq expression 'fail) nil)
        ((symbolp expression)
         (let ((pair (assoc expression bindings)))
           (if pair
               (progn
                 (assert (member (cdr pair) '(true fail)))
                 (eql 'true (cdr pair)))
               (error "No variable named ~A in the bindings." expression))))
        ((atom expression) (error "Invalid atom ~A in the expression." expression))
        (t (case (length expression)
             ((2) (destructuring-bind (op subexpression) expression
                    (case op
                      ((not) (not (evaluate-boolean subexpression bindings)))
                      (otherwise (error "Invalid operator ~A in ~A" op expression)))))
             ((3) (destructuring-bind (op left right) expression
                    (case op
                      ((and)  (and  (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      ((or)   (or   (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      ((nand) (nand (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      ((nor)  (nor  (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      ((xor)  (xor  (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      ((impl) (impl (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      ((equ)  (equ  (evaluate-boolean left bindings) (evaluate-boolean right bindings)))
                      (otherwise (error "Invalid operator ~A" op)))))
             (otherwise (error "Invalid expression ~A" expression))))))
我尝试了一些方法,但它们似乎都给出了报告缺失变量的错误

如何实现宏

  • 作为
    defmacro
    ,或
  • 求值布尔函数中使用
    macrolet

我通常先用
defun
defmacro
测试东西,然后用
flet
替换它。有什么建议吗?

既然你没有说你尝试了什么,我不知道你做错了什么,但我想你可能试图用宏调用替换
案例中的个别案例?这不起作用,因为外部宏(
大小写
)在内部宏之前展开,因此内部宏不能用于生成外部宏的语法(除非外部宏是专门编写的,这里不是这种情况)

因此,解决方案是编写一个宏,为您生成整个
案例。比如:

(macrolet ((ops-case (op-sym (&rest ops))
             `(case ,op-sym
                ,@(loop for op in ops
                        collect `((,op) (,op (evaluate-boolean left bindings)
                                             (evaluate-boolean right bindings))))
                (otherwise (error "Invalid operator ~A" ,op-sym)))))
  (ops-case op (and or nand nor xor impl equ)))
虽然我不相信这真的是个好主意。像这样的一次性宏会使代码更难理解,这也不会显著缩短代码。通常,您希望使用宏来抽象代码中多次出现的模式

更通用的方法可能是这样的:

(defmacro ecase-template (keyform template &body cases)
  `(ecase ,keyform
     ,@(loop for case in cases
             collect (sublis `((_ . ,case)) template))))
这将通过使用大小写中的值替换tempate中的下划线来生成大小写表达式。例如:

CL-USER> (macroexpand-1 '(ecase-template op
                             ((_) (_ (evaluate-boolean left bindings)
                                     (evaluate-boolean right bindings)))
                           and or nand nor xor impl equ))
(ECASE OP
  ((AND)
   (AND (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((OR)
   (OR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((NAND)
   (NAND (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((NOR)
   (NOR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((XOR)
   (XOR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((IMPL)
   (IMPL (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((EQU)
   (EQU (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))))

既然您没有说您尝试了什么,我不知道您做错了什么,但我猜您可能试图用宏调用替换
案例中的单个案例?这不起作用,因为外部宏(
大小写
)在内部宏之前展开,因此内部宏不能用于生成外部宏的语法(除非外部宏是专门编写的,这里不是这种情况)

因此,解决方案是编写一个宏,为您生成整个
案例。比如:

(macrolet ((ops-case (op-sym (&rest ops))
             `(case ,op-sym
                ,@(loop for op in ops
                        collect `((,op) (,op (evaluate-boolean left bindings)
                                             (evaluate-boolean right bindings))))
                (otherwise (error "Invalid operator ~A" ,op-sym)))))
  (ops-case op (and or nand nor xor impl equ)))
虽然我不相信这真的是个好主意。像这样的一次性宏会使代码更难理解,这也不会显著缩短代码。通常,您希望使用宏来抽象代码中多次出现的模式

更通用的方法可能是这样的:

(defmacro ecase-template (keyform template &body cases)
  `(ecase ,keyform
     ,@(loop for case in cases
             collect (sublis `((_ . ,case)) template))))
这将通过使用大小写中的值替换tempate中的下划线来生成大小写表达式。例如:

CL-USER> (macroexpand-1 '(ecase-template op
                             ((_) (_ (evaluate-boolean left bindings)
                                     (evaluate-boolean right bindings)))
                           and or nand nor xor impl equ))
(ECASE OP
  ((AND)
   (AND (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((OR)
   (OR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((NAND)
   (NAND (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((NOR)
   (NOR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((XOR)
   (XOR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((IMPL)
   (IMPL (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))
  ((EQU)
   (EQU (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))))

这可能不是您想要的,但是CLOS对于这种符号评估的调度来说是非常棒的

下面是一个(经过最低限度测试的)计算器实现,它使用一对通用函数(当然,对于您的小语言来说,这是
eval
apply
)以及一个宏,可以让您为
apply
通用函数定义“直接”方法。“direct”方法是一种简单地转换为包含子速率语言中具有相同名称的运算符的形式的方法(这基本上涵盖了代码中所有的大嵌套
大小写

(在某些情况下,它的工作原理与您的代码略有不同:例如,一旦找到绑定变量,它只需将其值放回计算器中,而不需要任何额外的特例技巧。)


这可能不是您想要的,但是CLOS对于这种符号评估的调度来说是非常棒的

下面是一个(经过最低限度测试的)计算器实现,它使用一对通用函数(当然,对于您的小语言来说,这是
eval
apply
)以及一个宏,可以让您为
apply
通用函数定义“直接”方法。“direct”方法是一种简单地转换为包含子速率语言中具有相同名称的运算符的形式的方法(这基本上涵盖了代码中所有的大嵌套
大小写

(在某些情况下,它的工作原理与您的代码略有不同:例如,一旦找到绑定变量,它只需将其值放回计算器中,而不需要任何额外的特例技巧。)


感谢您解释内部/外部宏的内容!感谢您解释内部/外部宏的内容!