Haskell中的短路(&&)

20
最近困扰我已久的一个问题:Haskell在返回布尔值的函数中,是否会执行所有等价测试,即使其中一个返回false的值?例如:
f a b = ((a+b) == 2) && ((a*b) == 2)

如果第一个测试返回false,Haskell会在&&之后执行第二个测试吗?还是Haskell懒惰到不执行它而继续前进?

4个回答

24
应该像其他语言一样进行短路计算。在 Prelude 中它是这样定义的:
(&&)                    :: Bool -> Bool -> Bool
True  && x              =  x
False && _              =  False

如果第一个参数为False,则无需评估第二个参数。


在列表推导式的情况下,这也是一样的吗? - Jonno_FTW
1
“短路评估”并不是正确的说法。你无法证明它是否会产生副作用,因此你也无法知道这些值是否被计算,但除非需要,否则它们不太可能被计算。 - Dario
1
我认为这与C++相同:语言规定右侧不会被评估。但是,如果编译器可以确定没有任何副作用,它可能仍然会对其进行评估 - 一些C++编译器会这样做,因为条件分支很昂贵。 - Jason Orendorff
1
请注意,在Haskell中评估thunk*可能会产生副作用:错误或无限循环。因此,就像C ++一样,如果编译器要评估它不应该评估的内容,它必须首先检查可能的副作用。 - Jason Orendorff
是的,在列表推导中它的工作方式相同。Haskell是一种纯函数语言。由于该函数未在单子中定义,因此不能产生副作用。一个好的测试可能是这样的:False && (length [1..]) < 0因此,请在右侧抛出永远无法完成的内容。 - Caleb

5

像马丁所说的那样,具有惰性求值的语言永远不会评估任何不需要立即价值的东西。在像Haskell这样的惰性语言中,您可以免费获得短路效应。在大多数语言中,||和&&等类似运算符必须特别构建到语言中,以使它们能够短路评估。然而,在Haskell中,惰性求值使这变得不必要。您甚至可以自己定义一个短路函数:

scircuit fb sb = if fb then fb else sb

此函数将像逻辑“或”运算符一样行为。以下是Haskell中如何定义||:

True  || _ = True
False || x = x

所以,针对你的问题给出具体答案是:不会。如果左侧条件为真,则右侧条件永远不会被执行。对于其他“短路”的运算符,你可以自己推理。

1

一个简单的测试,"证明"了Haskell确实具有短路功能,正如Caleb所说。

如果你尝试在一个无限列表上运行求和操作,你将会遇到堆栈溢出问题:

Prelude> foldr (+) 0 $ repeat 0
*** Exception: stack overflow

但是如果你在一个无限列表上运行例如(||)(逻辑或)操作,由于短路的原因,你很快就会得到结果:

Prelude> foldr (||) False $ repeat True
True

有趣。 对我来说,这有点难以接受,因为foldr使用第二个参数和列表中最后一个元素的函数。 在这种情况下,由于这是一个无限列表,想到应用这个函数有点奇怪。 不过还是谢谢你澄清了。 - user15277323

0

惰性求值意味着,直到真正需要时才会进行评估。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接