2

长期以来一直在为家庭作业做一个宏,但我们完全卡住了,我的老师时间有限,而且截止日期远远超过了限制。这是我们最后一次尝试解决这个问题。

说明如下:

我们将编写一个宏匹配模式,将参数 expr 与多个参数 pattern_i 进行匹配。如果成功,body_i 将在评估期间使用 pattern_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 循环所有模式,并通过将它们传递给我们的 match 函数,我们将返回 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 上有一个引用,但正文中的模式中没有引用,这是为什么呢?为什么身体里有:三个和:缺点?

4

2 回答 2

3

The common approach to this problem is following:

  1. Expand your matchers into a more elaborate intermediate representation, for example, a pattern and action pair (('a b) b) is translated into (match-action V1 (match-cons (match-quote a) (match-cons (match-bind b) (match-nil))) (progn b)).

  2. Implement a simpler macro match-action, which unrolls a tree of commands into a sequence of nested bindings and checks. Each failing check returns a special failure value. For example, (match-action V1 (match-bind x) x) is expanded into (let ((x V1)) x), or (match-action V1 (match-cons (match-bind a) (match-bind b)) (cons b a)) is expanded into (if (listp V1) (let ((V2 (car V1)) (V3 (cdr V1)) (a V2) (b V3)) (cons b a)) match-failure). Note that match-bind command must check if its argument is already in the context, and in this case it should translate into a structural equality check.

  3. Now, implementing a match macro is trivial - introduce a new variable (with gensym), bind it to the value of your expression, and apply matchers in sequence unless a matcher returns something different from match-failure.

I hope it's sufficient for you to implement your own matcher. Note that this approach is extensible, you can add more complicated patterns, like ellipsis, function matchers, regexps, and so on.

于 2013-01-20T13:27:49.450 回答
1

我记得回答了一个模式匹配问题,但是以一种实用的方式:使用通配符比较列表。以下答案保留了该答案的上下文。

你的宏可以做这样的事情:

  1. 评估expr一次成变量;
  2. 将每个(pattern &body body)子句扩展为调用cmp(或您的match),将结果保存在变量中,如果不是nil,则运行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(或您的match)以返回alist变量绑定,而不是仅t在匹配模式中有变量时返回。
于 2013-01-21T17:00:30.483 回答