使用GHCi断点调试器查找中间结果?

9

当函数的结果已知时,是否可以停止GHCi调试器?

例如,考虑以下代码片段:

blabla    :: [Int] -> Int
bla       :: Int -> Int
papperlap :: Int -> Int -> Int

bla x         = x+x
papperlap y x = ((y *) . bla) x
blabla xs     = foldl papperlap 0 x

现在,我想看到“papperlap”和“bla”的结果。但请记住,我希望在评估结果时停止。因此,使用“:force”是不可能的,因为它会改变评估顺序。
当我使用“:break”时,调试器会停止,但_result尚未被评估。请查看下面的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.
[1 of 1] Compiling Main             ( bla1.hs, interpreted )
Ok, modules loaded: Main.
*Main> :break blabla
Breakpoint 0 activated at bla1.hs:7:1-36
*Main> :break papperlap
Breakpoint 1 activated at bla1.hs:6:1-31
*Main> :break bla
Breakpoint 2 activated at bla1.hs:5:1-19
*Main> blabla [1,2,3]
Stopped at bla1.hs:7:1-36
_result :: Int = _
[bla1.hs:7:1-36] *Main> :step
Stopped at bla1.hs:7:17-36
_result :: Int = _
xs :: [Int] = [1,2,3]
[bla1.hs:7:17-36] *Main> :step
Stopped at bla1.hs:6:1-31
_result :: Int = _
[bla1.hs:6:1-31] *Main> :step
Stopped at bla1.hs:6:17-31
_result :: Int = _
x :: Int = 3
y :: Int = _
[bla1.hs:6:17-31] *Main> :step
Stopped at bla1.hs:6:1-31
_result :: Int = _
[bla1.hs:6:1-31] *Main> :step
Stopped at bla1.hs:6:17-31
_result :: Int = _
x :: Int = 2
y :: Int = _
[bla1.hs:6:17-31] *Main> :step
Stopped at bla1.hs:6:1-31
_result :: Int = _
[bla1.hs:6:1-31] *Main> :step
Stopped at bla1.hs:6:17-31
_result :: Int = _
x :: Int = 1
y :: Int = 0
[bla1.hs:6:17-31] *Main> :step
Stopped at bla1.hs:5:1-19
_result :: Int = _
[bla1.hs:5:1-19] *Main> :step
Stopped at bla1.hs:5:17-19
_result :: Int = _
x :: Int = 1
[bla1.hs:5:17-19] *Main> :step
Stopped at bla1.hs:5:1-19
_result :: Int = _
[bla1.hs:5:1-19] *Main> :step
Stopped at bla1.hs:5:17-19
_result :: Int = _
x :: Int = 2
[bla1.hs:5:17-19] *Main> :step
Stopped at bla1.hs:5:1-19
_result :: Int = _
[bla1.hs:5:1-19] *Main> :step
Stopped at bla1.hs:5:17-19
_result :: Int = _
x :: Int = 3
[bla1.hs:5:17-19] *Main> :step
0
*Main>
2个回答

3

也许对于您的即时调试问题来说有点晚了,但以下是我会做的:

blabla    :: [Int] -> Int
bla       :: Int -> Int
papperlap :: Int -> Int -> Int

bla x         = (undefined :: Int)
papperlap y x = ((y *) . bla) x
blabla xs = foldl papperlap 0 xs

现在我们知道在评估 bla 时会出现异常。所以让我们进入 ghci:

[1 of 1] Compiling Main             ( temp.hs, interpreted )
Ok, modules loaded: Main.
λ: :set -fbreak-on-exception
λ: :trace blabla [1,5,17]
Stopped at <exception thrown>
_exception :: e = _
λ: :hist
-1  : bla (temp.hs:6:17-35)
-2  : bla (temp.hs:6:1-35)
-3  : papperlap (temp.hs:7:17-31)
-4  : papperlap (temp.hs:7:1-31)
-5  : papperlap (temp.hs:7:17-31)
-6  : papperlap (temp.hs:7:1-31)
-7  : papperlap (temp.hs:7:17-31)
-8  : papperlap (temp.hs:7:1-31)
-9  : blabla (temp.hs:8:13-32)
-10 : blabla (temp.hs:8:1-32)
<end of history>
λ: :back
Logged breakpoint at temp.hs:6:17-35
_result :: a
λ: :list
5
6  bla x         = (undefined :: Int )
                   ^^^^^^^^^^^^^^^^^^^
7  papperlap y x = ((y *) . bla) x
λ: :back
Logged breakpoint at temp.hs:6:1-35
_result :: a
λ: :list
5
6  bla x         = (undefined :: Int )
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7  papperlap y x = ((y *) . bla) x
λ: :back
Logged breakpoint at temp.hs:7:17-31
_result :: Int
x :: Int
y :: Int
λ: :list
6  bla x         = (undefined :: Int )
7  papperlap y x = ((y *) . bla) x
                   ^^^^^^^^^^^^^^^
8  blabla xs = foldl papperlap 0 xs

因此,我们可以看到堆栈一直引导到对bla右侧进行评估。

这并不完美,因为到处放置undefined似乎有些俗气和低劣,但:hist已经给了您很多工作,使用:list可以使事情变得更加清晰。


1

Haskell不会自动为您遍历文字表达式。例如:

e = 2*(2+2)

这段代码会立即求值为8,因为编译器将优化在编译时找到的任何文字表达式(这种类型的表达式称为常量应用形式)。

现在你必须意识到foldl是惰性的。如果你在一个列表上调用foldl f,它不会执行一次f的计算,直到它被完全强制执行为止。

>foldl (+) 0 [1,2,3] 
>foldl (+) a1 [2,3]
     where a1 = 0+1
>foldl (+) a2 [3]
     where a2 = a1+2
           where a1 = 0+1

最终我们有了((0+1)+2)+3),编译器说:“好的,我们已经用尽了所有列表并将每个求值扩展到了最原始的形式。让我们来评估一下。”而我们已经知道Haskell不会遍历CAF的求值过程。
如果您想查看求值的中间值,您必须首先实际产生它们。您可以使用以下严格变体的foldl方法:
foldl' f z []     = z
foldl' f z (x:xs) = let z' = z `f` x 
                    in seq z' $ foldl' f z' xs

我会让你自己去理解它的工作原理,但是 seq a b 会在惰性地评估 b 之前完全评估 a

除此之外,其他的都照旧,只需将 foldl 改为 foldl'。当您在 foldl' 函数内暂停时,您将看到中间结果。


谢谢您的回答。然而,切换到严格评估不是我想要的,如果是那样的话,我也可以使用“:force”。我的意图是能够检查'bla'和'papperlap'的结果,在它们的结果值被评估后。并获得类似于以下结果:bla 1 = 2; papperlap 0 1 = 0; bla 2 = 4; papperlap 0 2 = 0; bla 3 = 6; papperlap 0 3 = 0; blabla [1,2,3] = 0 - Maarten Faddegon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接