我理解如果我有声明c = a AND b,如果a是假的,那么编译器就不会打扰评估b,它会知道结果,因为a已经是假的。
但是,如果我有一个递归函数和AND递归调用的函数会怎么样。
So myfunc input1 input2 = and[myfunc(input1),myfunc(input2)]
如果从上面的递归函数调用树中的任何一点返回的函数返回false,递归函数是否会调用terminate,而false值只会在原始调用点进行求值?
换句话说,它会使用上面的懒惰评估吗?
答案 0 :(得分:8)
是。事实上,and
的一个实现是递归的(并没有添加任何严格性):
and :: [Bool] -> Bool
and [] = True
and (x:xs) = x && and xs
要证明这是有效的,您可以将False
的无限列表传递给and
,并看到它返回
Prelude> and (repeat False)
False
但是,请注意,这不适用于True
的无限列表,因为它将永远查找False
,但永远不会找到。{/ p>
答案 1 :(得分:6)
简而言之,答案是肯定的,Haskell在递归函数中会很懒惰。
&&
的懒惰不是一个特例:它是其定义的结果:
(&&) :: Bool -> Bool -> Bool
True && y = y
False && _ = False
在这里,Haskell的懒惰意味着它可以匹配&&
的第一个参数,而不需要评估第二个参数来了解结果。
对于像and
这样的递归函数,我们有定义:
and :: [Bool] -> Bool
and [] = True
and (b:bs) = b && and bs
这是一个递归定义,Haskell很懒,因为只有在需要时才会评估非空列表中b
和bs
的值:在这种情况下,{{的定义1}}强制我们查看第一个元素&&
,如果它是b
,那么False
的其余部分就不必进行评估。
这里的教训是,懒惰是Haskell通过其模式匹配提供的东西:当消耗足够的输入来匹配模式时,其余的可以保持不被评估,直到需要它为止。
答案 2 :(得分:2)
我是Haskell的新手,但是,它会评估左侧的整个递归调用树,并且只有当它们最终返回true时才会继续评估右侧的递归调用。 / p>
实际上,在您最终使用my_func的结果之前,它不会评估其中任何一个,但是如果您已经强制评估my_func,则上述情况就会出现。
将其视为传递承诺,为其所做的一切努力。因此调用my_func的结果不是结果,它是一个发现的承诺。它可能甚至没有考虑过这个承诺的意义,直到必须这样做。只有这样才能进入并弄清楚它需要调用什么。
我可能会离开,但这就是我理解它的方式。
答案 3 :(得分:2)
如果从上面的递归函数调用树中的任何一点返回的函数返回false,递归函数是否会调用terminate,而false值只会在原始调用点进行求值?
您可以编写依赖于该行为的递归调用。例如,如果将给定谓词函数exists
应用于列表中的每个元素的结果为任何元素返回true
,则OCaml中的以下p
函数将返回true
: / p>
let rec exists p = function
| [] -> false
| x::xs -> p x || exists p xs
由于短路评估,只要p
第一次返回true
,就会停止对列表进行迭代。
换句话说,它会使用上面的懒惰评估吗?
请注意,这是short-circuit evaluation而不是懒惰。