【问题标题】:Is there any way to print a variable inside a function in Haskell?有没有办法在 Haskell 的函数中打印变量?
【发布时间】:2021-04-19 02:27:50
【问题描述】:

我是 Haskell 的初学者,具有命令式语言的背景,我想知道是否可以在 Haskell 函数中打印而不是在 main 中打印。

merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys)
    | x < y = x:(merge xs (y:ys))
    | otherwise = y:(merge (x:xs) ys)

我想在每次合并操作后查看列表的内容,但是当我尝试在此函数中插入 print 语句时出现错误。此代码是执行合并排序操作的程序的一部分,因此我无法在主函数中放置打印语句。我会使用 ghci,并从单独的文件中编译合并函数以手动查看合并函数的工作原理,但我很好奇是否可以在合并函数本身中查看列表的内容。

给定此合并功能,我将如何显示列表的内容?我是否需要以另一种方式重新编码整个合并函数才能查看列表的内容?如果有,会是什么样子?

【问题讨论】:

  • 你可能想使用traceShow函数
  • 一个可能的技巧,在需要时打印两个中间值,而当你不需要时不会强迫你进入IOstackoverflow.com/a/41791334/1364288 缺点是它迫使你在一个单子上多态地工作,并且您必须以“打开”的方式编写递归——只有在插入“仪器”后,才能在稍后使用fix 关闭它。

标签: haskell functional-programming mergesort


【解决方案1】:

一般来说:不,您不能仅从 Haskell 中的任何函数进行打印。这是因为正常的 Haskell 函数不会有任何副作用,打印就是一个例子。 (其他副作用:改变变量、播放声音、发送 HTTP 请求。) Haskell 中的任何副作用都必须包装在 IO 类型中,所以如果你想在这个函数中使用 print,你必须将整个函数包裹在IO:

merge1 :: (Ord a, Show a) => [a] -> [a] -> IO [a]
merge1 [] ys = return ys
merge1 xs [] = return xs
merge1 (x:xs) (y:ys)
    | x < y =
        let merged = x:(merge1 x (y:ys)) in do
            print merged
            return merged
    | otherwise =
        let merged = y:(merge1 (x:xs) ys) in do
            print merged
            return merged

如您所见,这非常冗长。这也意味着merge1 必须包含在IO 中,因此任何其他使用它的函数也必须包含在IO 中。 (事实上​​,这通常被视为一种优势,因为它允许您从函数的类型中推断出函数的行为:这意味着不在 IO 中的任何函数都不会产生副作用,反之亦然.)

但是,在您的特定情况下,有一条逃生路线。 Debug.Trace 模块包含大量用于打印到控制台的函数没有 IO,仅用于调试目的。基本函数是trace s x,它将字符串s打印到控制台并返回x。在你的情况下,我建议使用traceShowId x,它将打印x,然后返回它:

merge2 :: (Ord a, Show a) => [a] -> [a] -> [a]
merge2 [] ys = ys
merge2 xs [] = xs
merge2 (x:xs) (y:ys)
    | x < y = traceShowId $ x:(merge2 x (y:ys))
    | otherwise = traceShowId $ y:(merge2 (x:xs) ys)

【讨论】:

  • 你需要一个Show 约束。
  • @dfeuer 哎呀,我确实做到了!我现在就解决这个问题。
  • @angelolunar 嗯,我刚试了一下,看起来我的代码确实给出了这样的错误——但你的原始代码也给出了完全相同的错误。由于我刚刚复制了您的代码并添加了traceShowId,因此看起来错误最初出现在您的代码中,然后我将此错误带到了我的代码中。无论如何,这似乎只是一个小错字——看起来你在以| x &lt; y 开头的行中写了x 而不是xs
  • @angelolunar traceShowId 只是一个函数。你可以像调用任何其他函数一样调用它——traceShowId theValueYouWantToPrint。至于tracetraceShowId的区别:trace是‘底层’函数,可以这么说,和traceShowId x = trace (show x) x
  • @angelolunar $ 是 Haskell 中非常常见的运算符,在 Prelude 中定义为 f $ x = f x——也就是说,它只是函数应用的一种显式形式。 $ 的好处是它的优先级很低,所以经常可以用它来代替括号。在这种情况下,traceShowId $ y:(merge2 (x:xs) ys) 等价于 traceShowId (y:(merge2 (x:xs) ys))。如需更多信息,请参阅learnyouahaskell.com/…
猜你喜欢
  • 2016-05-31
  • 2016-04-11
  • 2019-03-29
  • 2021-04-28
  • 2011-01-10
  • 2022-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多