为什么Scheme宏中的递归会导致错误?
(define-syntax fact
(syntax-rules ()
[(_ 1 res) res]
[(_ n res) (fact (- n 1) (* n res))]
[(_ n) (fact n 1)]))
(fact 3)
这段代码在Scheme中用宏实现阶乘,但并不能工作。我猜问题出在递归上。这是不是意味着在宏中不能使用递归?如何解决我这个例子中的问题?或者在展开时进行计算应该怎么做?
解决方案
这不是宏的用途。宏是用来引入新的语法。通常在Scheme中我们有两种条件语法,if 和 cond。大多数实现把 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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。