它看起来不是无限递归,因为我有一个基本情况,每个递归调用都采用较小的列表arg1
。
#lang racket
(define a '("Hat" "Shoes"))
(define b '("Coat" "Hat"))
(define c '("Shirt" "Pants"))
(define-syntax func
(syntax-rules ()
((func arg1 arg2 ... n)
(if (or (empty? arg1) (empty? arg2) ...)
empty
(if (or (member (first arg1) arg2) ...)
(cons (string-append n "." (first arg1)) (func (rest arg1) arg2 ... n))
(cons (first arg1) (func (rest arg1) arg2 ... n))
)
)
)
)
)
(func a b c "prefix")
答案 0 :(得分:1)
您定义了一个宏func
。
然后使用宏:(func a b c "prefix")
。
使用宏时,宏扩展器会查找定义并将使用与输入模式匹配,并使用模板生成扩展。
此处(func a b c "prefix")
与(func arg1 arg2 ... n)
匹配。
所以arg1 = a, arg2 = (b c), and n = "prefix"
。
现在使用模板。在模板里面我发现:
(func (rest arg1) arg2 ... n))
让我们填写:
(func (rest a) b c "prefix"))
当然,模板的其余部分也已填写。
现在因为宏的输出使用了func
宏扩展器
现在需要扩展新用途:(func (rest a) b c "prefix"))
。
该扩展的输出将包含(func (rest (rest a)) b c "prefix"))
(以及更多)。宏扩展器现在扩展该表达式并获得(func (rest (rest (rest a))) b c "prefix"))
。
问题不在于func
使用func
,而是func
的参数大小并没有减少。它表明在编写递归宏时必须小心。
答案 1 :(得分:1)
不要为此使用宏。宏应该用于生成代码,但这里没有必要这样做。 特别对使用扩展到自己的宏的警惕 - 他们会递归扩展!正如soegaard所指出的那样,在这种情况下,你的宏无限扩展,产生无限量的代码。这显然非常糟糕。
请记住,宏在编译时运行,而不是在运行时运行,因此宏的扩展不可能依赖于运行时值。出于这个原因,你的“基本情况”在宏的上下文中没有意义:从宏的角度来看,没有必要停止扩展。
你想要的是一个普通的可变参数函数。在Racket / Scheme中执行此操作的语法如下所示:
(define (f . args)
; do something with args
)
在上面的示例中,args
将绑定到包含提供给f
的所有参数的列表。您可以使用它来实现func
作为函数。如果您还想要syntax-rules
提供的更具声明性的模式匹配功能,则应该查看match
。