为什么Scheme宏中的递归会导致错误?

编程语言 2026-07-09
(define-syntax fact
  (syntax-rules ()
    [(_ 1 res) res]
    [(_ n res) (fact (- n 1) (* n res))]
    [(_ n)     (fact n 1)]))

(fact 3)

这段代码在Scheme中用宏实现阶乘,但并不能工作。我猜问题出在递归上。这是不是意味着在宏中不能使用递归?如何解决我这个例子中的问题?或者在展开时进行计算应该怎么做?

解决方案

这不是宏的用途。宏是用来引入新的语法。通常在Scheme中我们有两种条件语法,ifcond。大多数实现把 if 当作语法的一部分,而 cond 只是一个宏。你不能指望宏来进行计算。下面是 (fact 3) 的展开:

(fact 3) ->  (fact 3 1) ; n is 3
(fact 3 1) -> (fact (- 3 1) (* 3 1)) ; n is 3, res is 1
(fact (- 3 1) (* 3 1)) -> (fact (- (- 3 1) 1) (* (- 3 1) (* 3 1))) ; n is (- 3 1), res is (* 3 1)
(fact (- (- 3 1) 1) (* (- 3 1) (* 3 1))) -> (fact (- (- (- 3 1) 1) 1) (* (* (- 3 1) (* 3 1)) (- (- 3 1) 1)))
...

所以如果你看看 (- (- 3 1) 1),它是1 1,但只有在求值时才会发生,而且那发生在语法扩展之后。基本情况永远不会命中,因为没有地方把第一个参数设为只是1。这是一个无限循环。

再看这段代码:

(define num 1)
(fact num)

由于宏完全是关于语法的,宏把 num 作为参数传入,尽管num是 1,但这在宏展开时并不是一个事实,因此你会得到相同的结果:

(fact num) ->  (fact num 1) ; n is num
(fact num 1) -> (fact (- num 1) (* num 1)) ; n is num, res is 1
(fact (- num 1) (* num 1)) -> (fact (- (- num 1) 1) (* (- num 1) (* num 1))) ; n is (- num 1), res is (* num 1)
(fact (- (- num 1) 1) (* (- num 1) (* num 1))) -> (fact (- (- (- num 1) 1) 1) (* (* (- num 1) (* num 1)) (- (- num 1) 1)))
...

因此 fact 应始终被实现为一个过程/函数。当你想到“这门语言应该有某种东西,可以让写这类东西比我现在写的更容易”时,那么你就有了成为宏候选的对象。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章