Haskell/除錯
除錯列印是除錯程式的一種常見方法。在命令式語言中,我們可以簡單地在程式碼中新增列印語句到標準輸出或某些日誌檔案中,以跟蹤除錯資訊(例如,特定變數的值或一些可讀的訊息)。然而,在 Haskell 中,我們只能透過 IO 單子輸出資訊;我們不希望僅僅為了除錯而引入它。
為了解決這個問題,標準庫提供了 Debug.Trace。該模組匯出一個名為 `trace` 的函式,它提供了一種方便的方式在程式的任何地方新增除錯列印語句。例如,這個程式列印傳遞給 `fib` 的所有引數,當它們不等於 0 或 1 時
module Main where
import Debug.Trace
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = trace ("n: " ++ show n) $ fib (n - 1) + fib (n - 2)
main = putStrLn $ "fib 4: " ++ show (fib 4)
以下是結果輸出
n: 4 n: 3 n: 2 n: 2 fib 4: 3
此外,`trace` 使得能夠跟蹤程式的執行步驟;也就是說,哪個函式首先被呼叫,其次被呼叫,等等。為此,我們註釋我們感興趣的函式部分,如下所示
module Main where
import Debug.Trace
factorial :: Int -> Int
factorial n | n == 0 = trace ("branch 1") 1
| otherwise = trace ("branch 2") $ n * (factorial $ n - 1)
main = do
putStrLn $ "factorial 6: " ++ show (factorial 6)
當以這種方式註釋的程式執行時,它將按註釋語句執行的順序列印除錯字串。該輸出可能有助於在缺少語句或類似情況的情況下定位錯誤。
如上所示,`trace` 可用於 IO 單子之外;實際上,它的型別簽名...
trace :: String -> a -> a
...表明它是一個純函式。然而,`trace` 確實 在列印有用資訊時執行了 IO。發生了什麼事?實際上,`trace` 使用了一種技巧來規避 IO 與純 Haskell 之間的分離。這體現在以下免責宣告中,可以在 `trace` 文件 中找到
`trace` 函式只應用於除錯或監視執行。該函式不是引用透明的:它的型別表明它是一個純函式,但它具有輸出跟蹤訊息的副作用。
使用 `trace` 的一個常見錯誤:在嘗試將除錯跟蹤融入現有函式時,人們不小心將要被 `trace` 列印的值包含在要列印的訊息中;例如,不要這樣做
let foo = trace ("foo = " ++ show foo) $ bar
in baz
這會導致無限遞迴,因為跟蹤訊息將在 bar 表示式之前被評估,這將導致 foo 按照跟蹤訊息和 bar 的順序進行評估,而跟蹤訊息將在 bar 之前被評估,如此無限迴圈。不要使用 `show foo`,而應在跟蹤訊息中使用 `show bar`
let foo = trace ("foo = " ++ show bar) $ bar
in baz
包含 `show` 的輔助函式可能很方便
traceThis :: (Show a) => a -> a
traceThis x = trace (show x) x
類似地,`Debug.Trace` 定義了一個 `traceShow` 函式,它“列印”它的第一個引數並評估為第二個引數
traceShow :: (Show a) => a -> b -> b
traceShow = trace . show
最後,像這樣的 `debug` 函式也可能有用
debug = flip trace
這將允許您編寫類似的程式碼...
main = (1 + 2) `debug` "adding"
...使除錯語句的註釋/取消註釋變得更容易。
| 此頁面是一個 存根。您可以透過 擴充套件它 來幫助 Haskell。 |