我目前正在研究OCAML,并且有一个关于用户定义的if-then的问题,例如:
let cond (c,t,e) =
match c with
| true -> t
| false -> e
在阶乘函数中使用时:
let rec fact n =
cond (n=0,1, n * fact (n-1))
直觉上,它似乎是正确的,但我知道它会抛出堆栈溢出错误。有人可以向我解释为什么会这样,以及这个用户定义的if-then如何与内置if-then不同?
基本上你的用户定义的条件不是懒惰的评估。在实际匹配发生之前,OCaml会尝试评估您传递的两个表达式 - 对于true
和false
案例。
例:
让我们假设我们试图评估fact 2
。
cond (2=0,1, 2 * fact (2-1))
。在3元组传递给cond
之前,必须对其进行全面评估。要做到这一点,Ocaml必须评估函数fact (2-1)
。
现在我们评估fact 1
。返回值是cond (1=0,1, 2 * fact (1-1))
。同样,我们需要知道fact (1-1)
的值,所以我们递归地计算它。
我们评估fact 0
。这里的问题开始显现。返回值是cond (0=0,1, 0 * fact (0-1))
,但是为了评估函数cond
,我们首先要评估它的参数 - 3元组。这使我们评估fact (0-1)
!
然后,我们正在评估fact -1
......
... fact -2
... fact -3
......并且堆栈溢出:)内置的if-then懒惰地评估它的参数:首先,它检查条件是真还是假,然后它相应地只选择一个分支来评估 - 这种行为称为惰性评估。
实际上OCaml有你可以用来避免这种不良行为的操作lazy
和force
,但可能只是坚持传统的if
更好。