为什么连懒惰的褶子都如此渴望?

Adr*_*May 2 haskell lazy-evaluation

这段代码:

import Data.Foldable
import Debug.Trace

factors :: [Bool]
factors = [True, True, False, True, True, True, True, True]

andy :: Bool -> Bool -> Bool
andy False _ = False
andy True False = False
andy True True = True

tandy :: Bool -> Bool -> Bool 
tandy a b = let c = a `andy` b in trace (show a ++ " & " ++ show b ++ " = " ++ show c) c

wonder :: Bool
wonder = foldl tandy True factors

Run Code Online (Sandbox Code Playgroud)

当我评估时这样说wonder

True & True = True  
True & True = True  
True & False = False
False & True = False
False & True = False
False & True = False
False & True = False
False & True = False
Run Code Online (Sandbox Code Playgroud)

但我希望它早点停止。我已经尝试过用一切可以想象到的东西来代替foldl和 来&&代替andy,但似乎从未得到提示。

在我看来,该行andy False _ = False不会邀请编译器评估第二个参数。我没有强迫任何严格的要求。这是怎么回事?甚至 C 也可以做得更好。

Car*_*arl 6

tandy尽管andy不是,但这两个参数都是严格的。这是因为追踪。您要求它在调用中显示两个输入trace,因此它必须评估两个参数。

考虑“tandy2”:

tandy2 :: Bool -> Bool -> Bool
tandy2 False _ = trace ("False & anything = False") False
tandy2 True b = trace ("True & " ++ show b ++ " = False") b
Run Code Online (Sandbox Code Playgroud)

它不会在跟踪中盲目地评估两个参数,而是小心地仅评估否则会评估的相同参数。这使得它实际上反映了与之前相同的严格属性andy

尝试使用tandy2with foldr,您会发现它一旦遇到 就停止计算False

$ ghci
GHCi, version 9.2.1: https://www.haskell.org/ghc/  :? for help
ghci> import Debug.Trace
ghci> let tandy2 False _ = trace ("False & anything = False") False ; tandy2 True b = trace ("True & " ++ show b ++ " = False") b
ghci> foldr tandy2 True []
True
ghci> foldr tandy2 True [True]
True & True = False
True
ghci> foldr tandy2 True [True, False]
False & anything = False
True & False = False
False
ghci> foldr tandy2 True [True, False, True]
False & anything = False
True & False = False
False
Run Code Online (Sandbox Code Playgroud)

就这样吧。False它永远不会多次评估分支。

  • @Carl——它可能取决于 Haskell 版本。(我使用的是 8.10.7。)你必须相信我——我盯着 OP 原始代码的 GHCi 输出,其中“tandy”被你的“tandy2”替换。在 GHCi 中并使用 `main = print Wonder` 进行编译,标志 `-O0` 给出了八个跟踪行;标志“-O2”仅给出三个。 (2认同)