所以我正在完成 Real World Haskell 的一些初始章节练习,我想知道 GHCi 中是否有一个选项可以让它在每个递归调用上显示带有参数的函数评估。例如,我写了一个简单版本的“map”,当我应用它时,我希望 GHCi 显示每个带有实际参数的递归调用(希望是表达式结果)。可以让我了解幕后发生的事情。
PS 在我写这篇文章时,我有一种感觉,它可能会受到 haskell 执行模型的懒惰的限制,如果我错了,请纠正我。
您可以为此使用引擎盖:
import Debug.Hood.Observe
map2 f [] = []
map2 f (x:xs) = f x : (observe "map2" $ map2) f xs
main = runO $ print $ map2 (+1) ([1..10] :: [Int])
当您运行它时,它将打印对 map2 的每个调用以及相应的参数和返回的结果。你会看到类似的东西:
.
.
.
-- map2
{ \ { \ 10 -> 11
, \ 9 -> 10
} (9 : 10 : [])
-> 10 : 11 : []
}
-- map2
{ \ { \ 10 -> 11
} (10 : [])
-> 11 : []
}
-- map2
{ \ _ [] -> []
}
有关更多信息,请查看示例。
我通常使用Debug.Trace:
import Debug.Trace
buggy acc xs | traceShow (acc,xs) False = undefined
buggy acc [] = acc
buggy acc (x:xs) = buggy (acc + x) xs
main = print $ buggy 0 [1..10]
这让我看到了 buggy 功能是如何工作的:
(0,[1,2,3,4,5,6,7,8,9,10])
(1,[2,3,4,5,6,7,8,9,10])
(3,[3,4,5,6,7,8,9,10])
(6,[4,5,6,7,8,9,10])
(10,[5,6,7,8,9,10])
(15,[6,7,8,9,10])
(21,[7,8,9,10])
(28,[8,9,10])
(36,[9,10])
(45,[10])
(55,[])
55
关键是有一个从不匹配的模式,但是在它不匹配的时候打印一些东西。这样它总是被评估(并因此打印调试信息),并且很容易附加到任何功能上。但是,如果您只想查看某些情况,也可以使其匹配,例如:
buggy acc [] = acc
buggy acc (x:xs) | traceShow (acc, x, xs) True = buggy (acc + x) xs
然后你只能在非基本情况下获得调试输出:
(0,1,[2,3,4,5,6,7,8,9,10])
(1,2,[3,4,5,6,7,8,9,10])
(3,3,[4,5,6,7,8,9,10])
(6,4,[5,6,7,8,9,10])
(10,5,[6,7,8,9,10])
(15,6,[7,8,9,10])
(21,7,[8,9,10])
(28,8,[9,10])
(36,9,[10])
(45,10,[])
55
YMMV。