是否有可能匹配R5RS macroes define-syntax / syntax-rules中的重复模式?

时间:2013-06-12 16:46:51

标签: macros scheme r5rs

这个R5RS宏是我尝试过的,几乎就是我想做的事情。 Racket或其他实现并不喜欢这个宏我想要魔术发生的地方。

(define-syntax quote-unique
  (syntax-rules (magic end)

    ;; end case
    ((quote-unique magic processed end)
     'processed)

    ;; finished iteration
    ((quote-unique magic (processed ...) sym1 end rest ... )
     (quote-unique magic (processed ... sym1) rest ... end))

    ;; match (doesn't work since racket doesn't like sym1 twice in template)
    ;; but I'm looking for the same expression twice
    ((quote-unique magic processed sym1 sym1 . rest )
     (quote-unique magic processed sym1 . rest))

    ;; rotate
    ((quote-unique magic processed sym1 sym2 rest ... )
     (quote-unique magic processed sym1 rest ... sym2))

    ;; start iteration
    ((quote-unique rest ...)
     (quote-unique magic () rest ... end))))

在Common Lisp中这很简单:

(defmacro quote-unique ( &rest xs )
  (labels ((remove-duplicates (lis)
        (if lis
        (if (member (car lis) (cdr lis))
                (remove-duplicates (cdr lis))
            (cons (car lis) (remove-duplicates (cdr lis)))))))

     (list 'quote (remove-duplicates xs))))

我也一直在阅读Define syntax primer并考虑is-eqv的实现?会指出我在正确的方向,但它似乎不是在那里定义的宏。

如果在R5RS编译时间不可能,那么如何用R6RS完成?

2 个答案:

答案 0 :(得分:3)

Chris Jester-Young答案的remove-id例子可在R5RS中表达:

(define-syntax remove-id
  (syntax-rules ()
    ((remove-id s (t ...))
     (letrec-syntax ((aux (syntax-rules (s)
                            ((aux p* ())
                             'p*)
                            ((aux p* (s . rest))
                             (aux p* rest))
                            ((aux (p (... ...)) (u . rest))
                             (aux (p (... ...) u) rest)))))
       (aux () (t ...))))))

(注意,(... ...)引用省略号不是严格的R5RS(仅R7RS),而是仅用于按给定顺序生成序列而不是反转。因此,通过添加另一个宏,您甚至可以抛弃省略号。)

我希望这个例子清楚地说明如何解决原始问题。如果可以使用卫生宏来解决某些问题,那么在使用可能在R6RS之后不会标准化的程序宏或宏设施之前,应该三思而后行。

答案 1 :(得分:2)

您无法使用syntax-rules执行此操作,但可以使用syntax-case使用free-identifier=?的警卫执行此操作。这是一个例子:

(define-syntax (remove-id stx)
  (syntax-case stx ()
    ((_ head ())
     #''())
    ((_ head (next tail ...)) (free-identifier=? #'head #'next)
     #'(remove-id head (tail ...)))
    ((_ head (next tail ...))
     #'(cons 'next (remove-id head (tail ...))))))

> (remove-id foo (foo bar baz qux foo bar))
; => (bar baz qux bar)

但是,当然,如果您要使用syntax-case,那么实现quote-unique的方法要简单得多(此实现使用Racket的自定义哈希表):

(require (for-syntax racket/dict))
(define-syntax (quote-unique stx)
  (define (id-dict ids)
    (foldl (lambda (id d)
             (dict-set d id #t))
           (make-immutable-custom-hash free-identifier=? (compose eq-hash-code syntax-e))
           (syntax-e ids)))
  (syntax-case stx ()
    ((_ ids ...)
     (with-syntax ((unique (dict-keys (id-dict #'(ids ...)))))
       #''unique))))