关于懒惰的'和'条款的评价

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什么?

Wil*_*sem 7

问题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块中使用此操作符)处理的方面之一.