【发布时间】:2015-11-04 12:43:23
【问题描述】:
我确信这与惰性评估有关,但我仍然无法解释为什么它会以这种方式运行。为什么在verboseAdd2 中评估右侧会反转调试跟踪输出?
这是代码:
import Debug.Trace
data BinaryTree = Empty | Node Int BinaryTree BinaryTree
instance Show BinaryTree where
show Empty = "_"
show (Node x Empty right) = unwords [ show x, show right]
show (Node x left right) = unwords [show left, show x, show right]
add :: Int -> BinaryTree -> BinaryTree
add v Empty = Node v Empty Empty
add v a@(Node x left right)
| v == x = a
| v < x = Node x (add v left) right
| v > x = Node x left (add v right)
verboseAdd :: Int -> BinaryTree -> BinaryTree
verboseAdd v a = trace ("[1] Adding v=" ++ show v) (add v a)
verboseAdd2 :: Int -> BinaryTree -> BinaryTree
verboseAdd2 v a = trace ("[2] Adding v=" ++ show v ++ " to " ++ show a) (add v a)
verbosePlus :: (Num a, Show a) => a -> a -> a
verbosePlus left right = trace (show left ++ " + " ++ show right)
(left + right)
main :: IO()
main = do
let seq = [1,2,3,4,5]
in do print $ foldr verbosePlus 0 seq
putStrLn ""
print $ foldr verboseAdd Empty seq
putStrLn ""
print $ foldr verboseAdd2 Empty seq
这是输出:
5 + 0
4 + 5
3 + 9
2 + 12
1 + 14
15
[1] Adding v=1
[1] Adding v=2
[1] Adding v=3
[1] Adding v=4
[1] Adding v=5
1 _ 2 _ 3 _ 4 _ 5 _
[2] Adding v=5 to _
[2] Adding v=4 to 5 _
[2] Adding v=3 to 4 _ 5 _
[2] Adding v=2 to 3 _ 4 _ 5 _
[2] Adding v=1 to 2 _ 3 _ 4 _ 5 _
1 _ 2 _ 3 _ 4 _ 5 _
【问题讨论】:
-
将您的代码放在问题的主体中 - 它不会很长,如果所有内容都在那里,问题会更加清晰。
-
是的,因为首先你只需要强制
v进行追踪,但第二个也强制a(记住a这是你表达的其余部分;)) - 虽然它将评估这个a所有其他痕迹发生;) -
注意第二个更接近正在发生的事情 - 你真的从最后一个开始,但是
foldr(或者更好的你返回的结构)是懒惰这一切都会以一个大笨蛋告终,只有在你开始做最后一场演出时才会得到评估("1 _ 2 _ 3 _ 4 _ 5 _") -
@Carsten 抱歉,我不小心覆盖了您的编辑 - 我们在完全相同的时间戳进行了编辑。随意回滚到您的版本;我已经在 Meta 上发布了关于此的内容,请参阅 meta.stackoverflow.com/questions/309441/…
-
@Carsten 做到了。毕竟,您不能真正期望在评估树“2 _ 3 _ 4 _ 5 _”之前打印消息“[2] 将 v=1 添加到 2 _ 3 _ 4 _ 5 _”。因此,将首先评估“2 _ 3 _ 4 _ 5 _”,但这会触发它自己的跟踪。递归地,我们有向后的痕迹。
标签: haskell lazy-evaluation trace evaluation