当函数的结果已知时,是否可以停止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>
答案 0 :(得分: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 :(得分:0)
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
会在继续懒惰地评估a
之前完全重新评估b
。
除了将foldl
更改为foldl'
之外,请像以前一样执行其他所有操作。当您逐步完成评估时,在foldl'
函数内暂停时会看到中间值。