Common Lisp宏模式匹配

时间:2013-01-20 13:02:58

标签: macros common-lisp

长期以来一直在研究宏观作业,但我们完全陷入困境,我的老师时间有限,截止日期已经超出限制。这是我们解决这个问题的最后一次尝试。

说明如下:

我们将编写一个宏匹配模式,将参数expr与许多参数pattern_i相匹配。 如果成功,则在评估期间使用pattern_i中的自由变量评估body_i。

  • 模式是s表达式
  • 必须完全匹配
  • 不能使用符号引用
  • Expr只应评估一次

    示例:

     * (match-pattern '((foo bar) foo baz)
          ((car . car)     `(:cons ,car ,car))
          ((one two three) `(:three ,one ,two ,three)))
    
     (:THREE (FOO BAR) FOO BAZ)
    

到目前为止我们的策略是这样的:

1

我们计划在宏中用于比较模式的匹配函数。 (也许不完全正确,但你得到了重点)

    (defun match (sexpr1 sexpr2)
      (cond ((and (consp sexpr1) (consp sexpr2)) 
             (and (match (first sexpr1) (first sexpr2))
                  (match (rest sexpr1)  (rest sexpr2))))
            ((and (atom sexpr1) (atom sexpr2))
             t)
            (t nil)))

2

我们想要针对expr循环所有模式,并通过将它们传递给我们的匹配函数,我们将返回true或nil。如果是真的,我们将expr分配给模式的主体。

(defmacro match-pattern (sexpr &body body)
  (cond
    ((match sexpr (car (car body))) (print "sexpr shall match with body here"))
    ((null (cdr body)) nil)
    (t `(match-pattern sexpr ,@(cdr body)))))

3

不知道匹配是如何工作的,我们尝试将#mapcar与匿名lambda函数结合使用,但没有比这更进一步。

这似乎是一种合理的方法吗? 报价有很多问题。在描述的例子中有关于expr的引用但不是关于身体模式中的引用,为什么会这样? 为什么会出现这样的问题:身体有三个缺点?

2 个答案:

答案 0 :(得分:3)

解决此问题的常见方法如下:

  1. 将匹配器扩展为更精细的中间表示,例如,模式和操作对(('a b) b)已转换为(match-action V1 (match-cons (match-quote a) (match-cons (match-bind b) (match-nil))) (progn b))

  2. 实现一个更简单的宏match-action,它将命令树展开为一系列嵌套绑定和检查。每次失败的检查都会返回特殊的失败值。例如,(match-action V1 (match-bind x) x)已扩展为(let ((x V1)) x),或(match-action V1 (match-cons (match-bind a) (match-bind b)) (cons b a))已扩展为(if (listp V1) (let ((V2 (car V1)) (V3 (cdr V1)) (a V2) (b V3)) (cons b a)) match-failure)。请注意,match-bind命令必须检查其参数是否已在上下文中,并且在这种情况下,它应转换为结构相等性检查。

  3. 现在,实现match宏是微不足道的 - 引入一个新变量(带gensym),将其绑定到表达式的值,并按顺序应用匹配器,除非匹配器返回与match-failure不同的东西。

  4. 我希望你能够实现自己的匹配器。请注意,此方法是可扩展的,您可以添加更复杂的模式,如省略号,函数匹配器,正则表达式等。

答案 1 :(得分:1)

我记得回答了一个模式匹配问题,但是以一种功能性方式:compare lists using wild cards。以下答案保留了答案的背景。

您的宏可以执行以下操作:

  1. expr评估为变量;
  2. 将每个(pattern &body body)子句展开为调用cmp(或您的match),将结果保存在变量中,如果不是nil,则运行{{1} }}。
  3. 示例:

    body

    鉴于此,您仍然需要:

    • 定义(defmacro match-pattern (expr &rest clauses) (let ((expr-var (make-symbol (symbol-name '#:expr))) (bindings-var (make-symbol (symbol-name '#:bindings)))) `(let ((,expr-var ,expr) (,bindings-var nil)) (declare (ignorable ,expr-var)) (cond ,@(mapcar #'(lambda (clause) (destructuring-bind (pattern &body body) clause `((setf ,bindings-var (cmp ,expr-var ,pattern)) (let (,@(mapcar #'(lambda (var) `(,var (cdr (assoc ',var ,bindings-var)))) (pattern-vars pattern))) ,@body)))) clauses))))) ;
    • 当匹配模式中存在变量时,调整pattern-vars(或您的cmp)以返回alist变量绑定而不仅仅match