Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/apache-kafka/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell'中的惰性评估;使用trace函数的dos表示法_Haskell_Lazy Evaluation - Fatal编程技术网

Haskell'中的惰性评估;使用trace函数的dos表示法

Haskell'中的惰性评估;使用trace函数的dos表示法,haskell,lazy-evaluation,Haskell,Lazy Evaluation,我想知道为什么这个“调试消息1”没有打印在这个代码段中: import Debug.Trace main = do return (trace "debug message 1" ()) trace "debug message 2" (return ()) 打印第二条“调试消息2”,但不打印“调试消息1”。看来这两种表达方式是一样的 我尝试将“调试消息1”绑定到一个变量,然后在另一个地方使用该变量,它实际上触发了计算并打印“调试消息1”,但我仍然不理解为什么会发生这种情况

我想知道为什么这个“调试消息1”没有打印在这个代码段中:

import Debug.Trace

main = do
    return (trace "debug message 1" ())
    trace "debug message 2" (return ())
打印第二条“调试消息2”,但不打印“调试消息1”。看来这两种表达方式是一样的

我尝试将“调试消息1”绑定到一个变量,然后在另一个地方使用该变量,它实际上触发了计算并打印“调试消息1”,但我仍然不理解为什么会发生这种情况

如果我翻转语句的顺序,结果仍然相同:

import Debug.Trace

main = do
    trace "debug message 2" (return ())
    return (trace "debug message 1" ())

“调试消息1”从未打印(使用runhaskell)。

我猜可能是因为“惰性评估”

请注意,您不会返回任何内容。换句话说,“返回”还没有被查询(没有返回),也没有用。在
return
语句中,您不在“一元”上下文中。因此没有理由对其进行评估,只需传递“调用树”作为结果

换言之,它会一直保留在“呼叫树”上,直到有人想接它

对于第二种情况,调用
跟踪
很简单。执行monad直到它到达一个“
返回
”,在到达该返回之前,将采取所有必要的操作,包括执行调试信息(如果需要)

ghci
中的示例:

$ ghci
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> import Debug.Trace
Prelude Debug.Trace> do return (trace "debug message 1" ())
Prelude Debug.Trace> do trace "debug message 2" (return ())
debug message 2
同样适用于
runhaskell
。如果您编写这两个程序:

程序1.hs

import Debug.Trace

main = do return (trace "debug message 1" ())
import Debug.Trace

main = do
    trace "debug message 2" (return ())
程序2.hs

import Debug.Trace

main = do return (trace "debug message 1" ())
import Debug.Trace

main = do
    trace "debug message 2" (return ())
然后控制台显示:

$ runhaskell program1.hs
$ runhaskell program2.hs
debug message 2
$ 
但是,如果您编写了一个
IO Bool
(因此带有返回值),并且以后使用该值,则将执行跟踪,例如:

testFun :: IO Bool
testFun = do
    trace "foo" $ return $ trace "Hello" True

main :: IO ()
main = do
    b <- testFun
    print b
但是,如果您省略
print b
并将
return()
放在后面,Haskell对返回的内容不感兴趣,因此不会打印跟踪:

testFun :: IO Bool
testFun = do
    trace "foo" $ return $ trace "Hello" True

main :: IO ()
main = do
    b <- testFun
    return ()   --we ran `testFun` but are not interested in the result

do
符号没有什么特别的魔力

main = do
    return (trace "debug message 1" ())
    trace "debug message 2" (return ())
就跟

main = return (trace "debug message 1" ()) >>=
        \_ -> trace "debug message 2" (return ())
main = trace "debug message 2" (return ()) >>=
         (\_ -> return (trace "debug message 1" ()))
根据其中一个单子身份法则,
返回a>=f=fa
,所以

main = (\_ -> trace "debug message 2" (return ()))
         (trace "debug message 1" ())
函数忽略其参数,因此不计算参数;表达式简化为

main = trace "debug message 2" (return ())
第一条消息已完全消失,您可以看到剩余的
跟踪
现在是最外层的应用程序,必须对其进行缩减以计算
main
,因此将打印此消息

当你翻动订单时,你得到了

main = do
    trace "debug message 2" (return ())
    return (trace "debug message 1" ())
这和

main = return (trace "debug message 1" ()) >>=
        \_ -> trace "debug message 2" (return ())
main = trace "debug message 2" (return ()) >>=
         (\_ -> return (trace "debug message 1" ()))

这里的情况有点复杂。第一个
跟踪
(消息2)是强制的,因为
>=
对于
IO
的左操作数是严格的。然后执行
return()
,什么也不做。忽略其值,并执行最后的操作
return(跟踪“调试消息1”()
)。这也不做任何事情(
return
从不做任何有趣的事情)。由于
main
操作的结束是程序的结束,因此不会检查此返回值,因此也不会强制执行此返回值,因此不会对其进行计算。有些人认为应该要求
main
具有类型
IO()
,以强调其返回值从未被使用。(我相信他们在这方面是错的,因为永远运行的程序应该有类型
iovoid
ioa
,但这是一个挑剔。)

如果我颠倒顺序,同样的事情也会发生
main=do跟踪“调试消息2”(返回());return(trace“debug message 1”(跟踪“debug message 1”)
@cmcdragokai:如果我在
ghci
中运行它,第二次,它将打印调试消息。在此上下文中不打印它的原因是因为它放在return语句之后。但是,如果您执行
do trace“debug message 2”(return())
,它将打印调试消息。请使用
runhaskell
运行它。更清楚的是,当我使用
runhaskell
运行它时,无论主语句的顺序如何,“debug message 1”从未打印过。@cmcdragokai:嗯
ghci
或多或少是
runhaskell
的等价物,但是是交互式的。如您所见,将打印第二条调试消息。但是很明显,您不能将它放在
return
语句之后。您可以在其中添加一个引用,上面写着“当程序执行时,将执行主计算,并丢弃其结果(t类型)”。回答得不错。那么,如果main的类型不是(你的结论暗示)IO(),那么它是什么呢?@cmcdragokai,对于任何
a
,它都可以是
ioa
。不管是什么,这一结果永远不会被要求。