rya*_*n91 4 haskell lazy-evaluation
我不明白为什么以下代码的行为方式如下:
myand :: Bool -> Bool -> Bool
myand True True = True
myand _ _ = False
containsAandB :: String -> IO Bool
containsAandB s = do
containsA <- do
putStrLn $ "Check if 'a' is in " ++ s
return $ 'a' `elem` s
containsB <- do
putStrLn $ "Check if 'b' is in " ++ s
return $ 'b' `elem` s
return $ containsA `myand` containsB
Run Code Online (Sandbox Code Playgroud)
这是我测试函数时的输出:
*Main> containsAandB "def"
Check if 'a' is in def
Check if 'b' in in def
False
Run Code Online (Sandbox Code Playgroud)
请注意,(&&)的行为就像'myand'一样,我只是编写了一个自定义函数来更好地可视化发生的事情.我对这'Check if 'b' is in def部分感到惊讶,因为containsA它已经是假的所以'myand'可以被评估,无论如何containsB.
问题1:
是否还有一个特殊原因containsB需要进行评估?我的理解是,containsB <- do ...除非需要,否则不会评估该语句,但我的猜测是,这可能表现不同,因为它是IO,因此不会产生副作用?
问题2:
在不处理嵌套子句的情况下,获得所需行为(如果containsA为false,containsB未检查)的最佳实践方法是if-else什么?
问题1: 为什么还必须对containsB进行评估?我的理解是,除非它是必需的,否则不会评估containsB < - do ...语句,但我的猜测是,这可能表现不同,因为它是IO,因此没有副作用?
你的实验是有缺陷的,因为你表演IO.其中一个重要方面是尊重陈述IO的顺序IO.因此,即使由于懒惰,我们不需要某个值,该IO部分也会被执行.
这在世界上是合乎逻辑的IO:假设我们读取了一个文件,我们有三个部分.我们阅读前两部分,然后我们阅读第三部分.但现在想象一下,由于懒惰,第二个IO命令永远不会被执行.那意味着第三部分实际上会读取文件的第二部分.
因此,在短期,由于IO,该声明被评估.但只有IO陈述.所以包裹在里面的价值return是不评估,除非你需要它.检查'b' `elem` s只在我们需要时才会发生.
然而,有一些方法可以" 欺骗 " IO这一点.例如trace(来自Debug.Trace)模块将执行" 不安全IO ":它将在给定评估时打印错误消息.如果我们写:
Prelude> import Debug.Trace
Prelude Debug.Trace> myand (trace "a" False) (trace "b" False)
Run Code Online (Sandbox Code Playgroud)
我们有:
Prelude Debug.Trace> myand (trace "a" False) (trace "b" False)
a
False
Run Code Online (Sandbox Code Playgroud)
问题2: 在不处理嵌套的if-else子句的情况下,获得所需行为的最佳实践方法是什么(如果containsA为false,则不检查containsB)?
如前所述,正常行为containsB是不评估的.但是,如果您执行IO操作,则必须在执行检查之前执行这些操作.这基本上是(>>=)操作员IO(您在do块中使用此操作符)处理的方面之一.