我学习了命令式语言的编程,主要是C ++和C语言,所以功能方法对我来说很新鲜。
当我以前编写函数/方法时,我通常采用“增量”方法(可能大多数人都这样做):编写一小部分代码,然后检查到目前为止结果是否符合预期(通常只是用printf或std :: cout将它们打印到stdout,改进算法,增强算法,然后检查到目前为止的结果是否符合预期(通常只需用printf或std :: cout将它们打印到stdout),精炼......我很少把整个方法写成一个整体。
这种“增量”方法的关键是具有诊断输出的能力(在上面的例子中是printf或std :: cout)。但是在Haskell中(据我现在所理解的),如果我想 - 比如说 - 使用'putStrLn'向stdout写一些东西,我必须改变我的函数的签名,因为'putStrLn'只返回一个IO Monad包含我想要打印的信息,但是在调用'putStrLn'时不打印它,对吧?因此,每次我想使用'putStrLn'进行诊断输出时,我都必须更改当前函数的签名以及我所有其他函数调用它的方式等...
有没有一种便宜又简单的方法将函数的'局部变量'的值打印到标准输出?
或者仅仅是因为我要求这个标志我不理解Haskell编程的基本部分?
答案 0 :(得分:13)
没有好办法做你想做的事。你可以接近Debug.Trace
,但我不建议在学习时因为Haskell的非标准评估顺序。 Haskell不能像C和C ++这样的语言顺序设置“变量”的值。因为它是惰性的,所以Haskell表达式按照依赖于使用的顺序进行评估,因此增量值的东西并不真正起作用。
Haskell是一种面向表达式的语言。使用它对您有利:
结合以上建议。您可以使用:t
获取GHCi中表达式的类型。
答案 1 :(得分:8)
这很奇怪,因为我发现你习惯使用的语言中没有Read Eval Print Loop(REPL),我一直很沮丧,因为我一直在测试代码的工作量。 REPL是我增量代码开发的基础;您可以使用它来测试代码,而无需添加一堆打印语句。
:r
并使用各种输入进行测试。偶尔你会陷入漫长的monadic计算或其他事情。 GHCi允许您设置断点 - 优先使用这些断点来向代码中添加print语句,因为您可以在不编辑代码的情况下搞砸并进行更多调查,最重要的是,您不需要在类型签名上添加Show约束。 / p>
完成后,您可以手动无意中内联辅助函数并使用ghc -O2
进行编译。
(根据我的经验,使用手动添加的打印语句或Debug.Trace
模块是完全痛苦的。)
摘要:尽可能避免在测试时编辑代码。使用GHCi很多。
答案 2 :(得分:1)
可以使用Debug.Trace模块的trace
函数快速将不纯的调试输出添加到纯函数中。它是一个函数,它返回第二个参数,并在强制第二个参数/返回值时打印第一个参数。
我认为暂时使用它进行调试是完全可以接受的,只要它不会在任何最终提交或其他永久代码中结束。此外,打印消息的顺序与评估顺序相匹配,这对调试也很有用,但并不总是输出的首选顺序。
如果您需要经常使用它,它也可能表示您需要将代码分解为更小的函数,这样可以通过仅指定参数和查看返回值来更轻松地检查其行为。
答案 3 :(得分:1)
首先,您可以通过将函数加载到ghci
并在那里使用它们来调试函数。
然后,您可以使用Debug.Trace
中的trace
在计算表达式时打印字符串。但请注意,由于Haskell使用延迟评估,因此在大多数情况下,表达式将在不同于您期望的时间进行评估。另请参阅Wikibooks和Haskell wiki。 (内部trace
使用不安全的调用,即使在纯代码中也允许它打印输出。通常你不应该使用它们,但在这种特殊情况下它是可以的。)
答案 4 :(得分:0)
如何构建与您描述的内容类似的内容,有一个相当简短的示例here。如果我正确地阅读它,作者创建了一个简单的monad,允许你在计算过程中打印出来,可以这么说。
答案 5 :(得分:0)
逼近:
GHCi调试是打印局部变量而不会使代码混乱的方法。
如果您返回跟踪值并与结果配对,WriterT monad变换器(严格或惰性)可以序列化您的日志。
{-# LANGUAGE PackageImports #-}
-- import qualified "transformers" Control.Monad.Trans.Writer.Strict as W
import qualified "transformers" Control.Monad.Trans.Writer.Lazy as W
compute:: Int -> Int -> (Int, Int)
compute x y = (result, local)
where
local = 2 * x
result = local + y
test :: (Monad m) => W.WriterT String m Int
test = do
let (r1, local1) = compute 5 3
W.tell $ "local1= " ++ show local1 ++ "\n"
let (r2, local2) = compute 2 2
W.tell $ "local2= " ++ show local2 ++ "\n"
return $ r1 + r2
main = do
(r, logs) <- W.runWriterT test
putStrLn logs
putStrLn $ "result= " ++ show r
输出:
local1= 10 local2= 4 result= ...