使用 GHCi Haskell 跟踪时,如何避免交织输出?

When using GHCi Haskell trace, how does one avoid interleaving output?

在使用 The Glorious Glasgow Haskell 编译系统 8.0.2 版时,我正在尝试向以下脚本添加跟踪信息:

import Debug.Trace
init :: Show a => [a] -> [a]
init l@(x:xs) | trace
          (
            if (length l) < 2
               then ( "\n\tinit [" ++ show x ++ "] = []" )
               else ( "\n\tinit " ++ show l ++ " = " ++ show x ++ " : (init " ++ show xs ++ ")" )
          ) False = undefined
init [_] = []
init (x:xs) = x : (Main.init xs)

但是,trace 的输出与计算表达式 Main.init [2,3,5,7] 的输出交织在一起,如下所示:

C:\>ghci my_script.hs
GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( my_script.hs, interpreted )
Ok, modules loaded: Main.
*Main> Main.init [2,3,5,7]

        init [2,3,5,7] = 2 : (init [3,5,7])
[2
        init [3,5,7] = 3 : (init [5,7])
,3
        init [5,7] = 5 : (init [7])
,5
        init [7] = []
]
*Main> :quit
Leaving GHCi.

如何导致输出中省略以下四行?

[2
,3
,5
]

我已经尝试使用 :set -fno-print-bind-result 的几种不同变体,如 https://downloads.haskell.org/~ghc/6.8.1/docs/html/users_guide/interactive-evaluation.html 所示,但无济于事。

这些行交错排列的原因是因为该列表是您的 Main.init 函数的实际 return 值。为了能够看到跟踪,您需要强制执行 Main.init 函数。您可以通过强制执行严格性(seqdeepseq)来做到这一点,但我认为最简单的方法是以不交错中间结果的方式简单地使用结果。

如果你只是想要没有交错数据的跟踪,但你不关心最后的数据,那么使用sum如何使用列表。

我建议你试试 ghci:

λ > sum (Main.init [2,3,5,7])

    init [2,3,5,7] = 2 : (init [3,5,7])

    init [3,5,7] = 3 : (init [5,7])

    init [5,7] = 5 : (init [7])

    init [7] = []
10

其他方法:

由于跟踪输出到 stderr 而不是 stdout,您可以在命令行上拆分输出:

➜  ~ ghc temp.hs -e "Main.init [2, 3, 5, 7]" > /dev/null

    init [2,3,5,7] = 2 : (init [3,5,7])

    init [3,5,7] = 3 : (init [5,7])

    init [5,7] = 5 : (init [7])

    init [7] = []

GHCi 隐式使用 print,并且由于懒惰,它会在评估列表元素时打印它们。你可以用一个强制整个列表的函数来组合它。

force :: [a] -> [a]
force xs = go xs `seq` xs
  where go [] = ()
        go (x : xs) = x `seq` go xs

在 ghci 中:

> print . force $ init [2, 3, 5, 7]

force也以更通用的形式存在于deepseq库中, 有了它,您可以使用 -interactive-printprint . force 设置为默认打印机。