为何这段递归阶乘Racket代码会引发无限递归?
为什么自定义的
my-if-bad会导致阶乘函数无限递归? 核心原因在于Racket的求值规则差异:普通函数的参数是严格求值(应用序),而内置if是特殊形式,采用按需求值(也就是你说的“惰性求值”)。
先明确两个关键概念:
- 应用序求值:调用普通函数时,会先把所有参数表达式全部计算出结果,再将结果传入函数体内执行。
- 特殊形式的按需求值:像内置
if这种特殊语法,不遵循普通函数的求值规则——它只会先计算条件表达式,然后根据条件的真假,只计算对应分支的表达式,另一个分支完全不会被求值。
拆解你的代码问题:
my-if-bad是普通函数,不是特殊形式
当调用(my-if-bad x y z)时,Racket会先把x、y、z三个参数的表达式全部计算完毕,再进入函数体执行里面的内置if。这就意味着,不管x是真还是假,y和z都会被提前求值。factorial-bad的无限递归过程
比如调用(factorial-bad 5):- 首先要计算
my-if-bad的三个参数:- 第一个参数
(= 5 0)得到#f; - 第二个参数
1得到1; - 第三个参数
(* 5 (factorial-bad 4))——这里会触发递归调用(factorial-bad 4);
- 第一个参数
- 调用
(factorial-bad 4)时,同样会先计算第三个参数(* 4 (factorial-bad 3)),继续递归; - 哪怕当
n减到0时,调用(factorial-bad 0)依然会先计算第三个参数(* 0 (factorial-bad -1)),然后继续递归到n=-1、n=-2……永远无法终止,因为递归分支在条件判断之前就被强制触发了。
- 首先要计算
对比内置if的正确行为
如果直接用内置if写阶乘:
(define (factorial-good n) (if (= n 0) 1 (* n (factorial-good (- n 1)))))
当n=0时,内置if只会计算条件(= 0 0)得到#t,直接返回1,完全不会去计算(* 0 (factorial-good -1)),递归到此终止,不会出现无限循环。
内容的提问来源于stack exchange,提问作者jem do
相关产品推荐
相关产品推荐

