macros - 如何在 lisp 中用 if 形式定义递归 cond 宏?

标签 macros scheme lisp common-lisp guile

我想用 if 实现 cond(在诡计中使用 lisp 宏),这是我的尝试:

(define-macro (cond . clauses)
  (if (pair? clauses)
      (let ((first (car clauses)) (rest (cdr clauses)))
         `(if ,(car first)
              (begin
                 ,@(cdr first))
              ,(if (equal? (caar rest) 'else)
                  ',(cadr rest)
                   `(cond ,rest))))))

但是当我用这段代码调用它时它不起作用:

(cond ((= 1 0) (display "hello"))
      ((= 1 1) (display "world"))
      (else
        (display "foo")))

我遇到了这个错误:

ERROR: In procedure car: Wrong type argument in position 1 (expecting pair): ()

为什么会出现此错误以及如何解决?我更喜欢使用 lisp 宏的解决方案。

最佳答案

大多数 Scheme 程序员,包括我自己,都不喜欢使用 define-macro,因为它完全不卫生。我不知道你为什么喜欢使用它们。考虑到这一点(我自己不会编写任何 define-macro 宏),我针对 its implementation of cond 研究了 Femtolisp(一种类似 Scheme 的实现,也不使用 hygienic 宏)。 :

(define-macro (cond . clauses)
  (define (cond-clauses->if lst)
    (if (atom? lst)
        #f
        (let ((clause (car lst)))
          (if (or (eq? (car clause) 'else)
                  (eq? (car clause) #t))
              (if (null? (cdr clause))
                  (car clause)
                  (cons 'begin (cdr clause)))
              (if (null? (cdr clause))
                  ; test by itself
                  (list 'or
                        (car clause)
                        (cond-clauses->if (cdr lst)))
                  ; test => expression
                  (if (eq? (cadr clause) '=>)
                      (if (1arg-lambda? (caddr clause))
                          ; test => (lambda (x) ...)
                          (let ((var (caadr (caddr clause))))
                            `(let ((,var ,(car clause)))
                               (if ,var ,(cons 'begin (cddr (caddr clause)))
                                   ,(cond-clauses->if (cdr lst)))))
                          ; test => proc
                          (let ((b (gensym)))
                            `(let ((,b ,(car clause)))
                               (if ,b
                                   (,(caddr clause) ,b)
                                   ,(cond-clauses->if (cdr lst))))))
                      (list 'if
                            (car clause)
                            (cons 'begin (cdr clause))
                            (cond-clauses->if (cdr lst)))))))))
  (cond-clauses->if clauses))

希望对你有用!


如果您喜欢的不是老式的不卫生的宏,而只是一个允许您以原始方式处理传入表单的宏系统,那么许多 Scheme 实现都提供了一个显式重命名 (ER) 宏系统,它允许您直接操作表单并且仍然允许您通过(顾名思义)显式重命名任何应该被宏调用站点保护免受阴影的标识符来保持卫生。这是 Chibi Scheme's implementation of cond :

(define-syntax cond
  (er-macro-transformer
   (lambda (expr rename compare)
     (if (null? (cdr expr))
         (if #f #f)
         ((lambda (cl)
            (if (compare (rename 'else) (car cl))
                (if (pair? (cddr expr))
                    (error "non-final else in cond" expr)
                    (cons (rename 'begin) (cdr cl)))
                (if (if (null? (cdr cl)) #t (compare (rename '=>) (cadr cl)))
                    (list (list (rename 'lambda) (list (rename 'tmp))
                                (list (rename 'if) (rename 'tmp)
                                      (if (null? (cdr cl))
                                          (rename 'tmp)
                                          (list (car (cddr cl)) (rename 'tmp)))
                                      (cons (rename 'cond) (cddr expr))))
                          (car cl))
                    (list (rename 'if)
                          (car cl)
                          (cons (rename 'begin) (cdr cl))
                          (cons (rename 'cond) (cddr expr))))))
          (cadr expr))))))

主要的 Scheme 实现通常根据它们用于低级宏的内容分为两个阵营:syntax-case 和显式重命名。 Racket、Chez Scheme、Guile 等使用syntax-case。 CHICKEN、MIT Scheme、Chibi Scheme等使用显式重命名。所以你将无法在 Guile 中使用上面的显式重命名版本,因为它属于 syntax-case 阵营。

关于macros - 如何在 lisp 中用 if 形式定义递归 cond 宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38414925/

相关文章:

c++ - 没有主体但参数名称不同的类函数宏

python - django 模板中的宏

objective-c - __VA_ARGS__ 宏扩展

scheme - 如何从方案中的列表中删除元素

python - 在 Python 中跟踪函数调用 + 闭包(à la SICP)的数量

Lisp函数调用错误

c - 迭代 GList 的宏

lisp - 在 r5rs 方案(drracket)中实现长度

Windows 7 上的 LispWorks 无法加载 Quicklisp

lisp - 需要了解带递归的 LISP 程序