我在课堂上被告知,以下函数不是尾递归,因为布尔运算符在递归调用之后被评估:
let rec exists p = function
[] -> false
| a::l -> p a || exists p l
但这并没有把堆栈炸到千万大小的列表上,更重要的是,它是标准库中的实现。如果它不是尾递归,就没有理由使用这种形式来代替看似等效且明显的尾递归
let rec exists p = function
[] -> false
| a::l -> if p a then true else exists p l
所以看起来 OCaml 编译器能够在像这样的简单情况下优化布尔运算以利用尾递归。但我注意到,如果我像这样切换操作数的顺序
let rec exists p = function
[] -> false
| a::l -> exists p l || p a
那么堆栈确实在 10m 个元素上被炸毁。所以看起来 OCaml 只有在递归调用出现在右侧时才能利用这一点,这让我怀疑编译器所做的只是用等效if
表达式替换布尔运算。有人可以证实或反驳这一点吗?