GHCi调试器没有递归地命中断点:为什么,以及解决方案是什么?

时间:2016-08-23 19:29:05

标签: haskell ghci

我有一个简单的递归程序,并不是很有效。因此,我尝试使用ghci调试器来确定发生了什么。我在所有函数行recurseprogressshaded上设置了断点,它捕获了前几个函数。但当我到达progress的第二行时,每次调用:继续让我回到同一行代码上,即使我正在调用recurseshaded在那里,并期待我的断点工作。这是代码:

import System.Environment

type Pos = (Int,Int) 
type Acc = ([[Pos]], [Pos])

main = do
  getArgs >>= putStrLn . show . length . combos . read . head

combos n = recurse n [] (allPos n) []

recurse :: Int -> [[Pos]] -> [Pos] -> [Pos] -> [[Pos]]
recurse n done avail inProg
   | length inProg == n = inProg:done
   | null avail = done
   | otherwise = fst $ foldr (progress n inProg) (done,avail) avail

progress :: Int -> [Pos] -> Pos -> Acc -> Acc
progress n inProg pos (done, avail)  =
  (recurse n done (filter (not . shaded pos) remain) (pos:inProg), remain)
  where remain = tail avail

allPos n =  [ (i,j) | i <- [0..n-1], j <- [0..n-1] ]

shaded :: Pos -> Pos -> Bool
shaded (i,j) (k,l) =
  k == i
  || l == j
  || k+l == i+j
  || k-l == i-j
  || abs (k-i) < 3 && abs (l-j) < 3

为什么ghci调试器不会在progress调用的函数中的断点处停止?是否有类似&#34;非重入&#34;把它们关掉?如何在每次递归调用这些函数时让调试器中断?

GHCi版本7.8.4

更新:我怀疑这可能与缓存结果有关,但对我来说这些功能已使用相同的参数调用两次并不明显。可能是我的代码中的错误?

2 个答案:

答案 0 :(得分:4)

我认为它按预期工作,但你必须考虑延迟评估。

如果你在recurse(第13 - 15行)的保护线和正在进行的递归调用(第19行)中断,当程序以{{1}运行时,你会看到这种模式}:

:main 2

但是,如果您更改Stopped at prog0.hs:13:6-23 - recurse Stopped at prog0.hs:14:6-15 - recurse Stopped at prog0.hs:15:18-67 - recurse, call to foldr progress Stopped at prog0.hs:19:3-74 - in progress, pos = (1,1) Stopped at prog0.hs:19:3-74 - in progress, pos = (1,0) Stopped at prog0.hs:19:3-74 - in progress, pos = (0,1) Stopped at prog0.hs:19:3-74 - in progress, pos = (0,0) Stopped at prog0.hs:13:6-23 - in recurse Stopped at prog0.hs:14:6-15 Stopped at prog0.hs:13:6-23 - in recurse Stopped at prog0.hs:14:6-15 Stopped at prog0.hs:13:6-23 - in recurse Stopped at prog0.hs:14:6-15 Stopped at prog0.hs:13:6-23 - in recurse Stopped at prog0.hs:14:6-15 以强制其结果:

progress

然后断点模式是:

import Control.DeepSeq

progress n inProg pos (done, avail)  = let
  result = (recurse n done (filter (not . shaded pos) remain) (pos:inProg), remain)
  in deepseq result result
  where remain = tail avail

Stopped at prog1.hs:14:6-23 - recurse Stopped at prog1.hs:15:6-15 - recurse Stopped at prog1.hs:16:18-67 - recurse, call to foldr progress Stopped at prog1.hs:21:6-26 - progress, pos = (1,1) Stopped at prog1.hs:14:6-23 - recurse Stopped at prog1.hs:15:6-15 Stopped at prog1.hs:21:6-26 - progress, pos = (1,0) Stopped at prog1.hs:14:6-23 - recurse Stopped at prog1.hs:15:6-15 Stopped at prog1.hs:21:6-26 - progress, pos = (0,1) Stopped at prog1.hs:14:6-23 - recurse Stopped at prog1.hs:15:6-15 Stopped at prog1.hs:21:6-26 - progress, pos = (0,0) Stopped at prog1.hs:14:6-23 - recurse Stopped at prog1.hs:15:6-15 的递归调用现在是交错的,因为我们正在立即强制它们。

答案 1 :(得分:3)

我正在添加另一个答案来演示如何使用Debug.Trace使用纯函数执行“printf”调试。

以下是recurseprogress,其中添加了跟踪语句以供打印 他们的电话参数。我们可以安全地使用undefined,因为 跟踪调用将始终返回False。

import System.Environment
import Debug.Trace

...

recurse :: Int -> [[Pos]] -> [Pos] -> [Pos] -> [[Pos]]
recurse n done avail inProg
   | trace msg False = undefined
   | length inProg == n = inProg:done
   | null avail = done
   | otherwise = fst $ foldr (progress n inProg) (done,avail) avail
   where msg = unwords ["recurse:", show n, show done, show avail, show inProg]

progress :: Int -> [Pos] -> Pos -> Acc -> Acc
progress n inProg pos (done, avail)
  | trace msg False = undefined
    where msg = unwords ["progress:", show n, show inProg, show pos, show (done, avail)]
progress n inProg pos (done, avail)  =
  (recurse n done (filter (not . shaded pos) remain) (pos:inProg), remain)
  where remain = tail avail

...

这是:main 2的输出:

recurse: 2 [] [(0,0),(0,1),(1,0),(1,1)] []
progress: 2 [] (1,1) ([],[(0,0),(0,1),(1,0),(1,1)])
recurse: 2 [] [] [(1,1)]
progress: 2 [] (1,0) ([],[(0,1),(1,0),(1,1)])
recurse: 2 [] [] [(1,0)]
progress: 2 [] (0,1) ([],[(1,0),(1,1)])
recurse: 2 [] [] [(0,1)]
progress: 2 [] (0,0) ([],[(1,1)])
recurse: 2 [] [] [(0,0)]

一般情况下,跟踪语句添加评估会以严格的方式进行,因为打印出函数参数会强制它们。