我正在尝试在haskell函数中调用'print out'函数。
(一个简单的调试消息)。
以下是我的编译器代码和错误消息(ghc 6.10)。
我不太明白为什么它会把puttr调用和空数组混为一谈。
空数组是该特定情况的返回值(打印输出消息实际上只是一个存根)。
知道为什么这不起作用?
由于
我的代码:
isAFactor :: Integer -> Integer -> Bool isAFactor x y = x `mod` y == 0 findFactors :: Integer -> Integer -> [Integer] findFactors counter num = let quotient = div num 2 in if(counter > quotient) then do putStrLn ("factorList is : " ++ show quotient) (*** Line 10***) [] else if(isAFactor num counter) then [counter] ++ [quotient] ++ findFactors (counter + 1) num else findFactors (counter + 1) num
来自ghc的错误
test.hs:10:4: Couldn't match expected type `[a] -> [Integer]' against inferred type `IO ()' In the expression: putStrLn ("factorList is : " ++ show quotient) [] In the expression: do putStrLn ("factorList is : " ++ show quotient) [] In the expression: if (counter > quotient) then do putStrLn ("factorList is : " ++ show quotient) [] else if (isAFactor num counter) then [counter] ++ [quotient] ++ findFactors (counter + 1) num else findFactors (counter + 1) num
答案 0 :(得分:20)
重要的是要记住Haskell是一种pure函数式语言。这意味着不允许函数产生任何副作用,包括将调试消息打印到屏幕上。
然而,可以打破这种纯度,它可以用于调试。看看模块Debug.Trace。在那里你会找到一个函数trace :: String -> a -> a
。您可以在代码中使用它,如下所示:
import Debug.Trace
isAFactor :: Integer -> Integer -> Bool
isAFactor x y = x `mod` y == 0
findFactors :: Integer -> Integer -> [Integer]
findFactors counter num =
let quotient = div num 2
in
if(counter > quotient)
then trace ("factorList is: " ++ show quotient) []
else if(isAFactor num counter)
then [counter] ++ [quotient] ++ findFactors (counter + 1) num
else
findFactors (counter + 1) num
如评论所示:
Haskell也是一种lazy语言。在实际需要结果之前不评估表达式。在延迟设置中使用跟踪功能可能会有点混乱,因为跟踪消息打印到屏幕时(如果它完全打印)并不总是很容易理解。
由于haskell是一种非常不同的语言,因此最好以同样不同的方式尝试开发程序。尝试推断您的功能,而不是使用trace
和类似的“不纯”结构。学习利用haskells强大的类型系统并使用(例如)QuickCheck来测试你的函数,一旦它通过了类型检查器。
答案 1 :(得分:9)
Jonas的帖子很好地涵盖了你的问题,所以我会给你一个惯用的重写你的findFactors函数。我第一次学习时发现它对我有帮助。
因此,您希望通过查看n
到1
中的每个数字来查找给定数字n/2
的所有因素,并检查它是否为{{1}的因子并建立一个列表。
您的版本(只需稍加修改即可开始使用):
n
进行了一些格式更改,使其更具可读性:
findFactors :: Integer -> Integer -> [Integer]
findFactors counter num =
let quotient = div num 2
in
if(counter > quotient)
then []
else if(isAFactor num counter)
then [counter] ++ findFactors (counter + 1) num
else
findFactors (counter + 1) num
这很好,但在某些方面它并不理想。首先,它会在每次调用findFactors :: Integer -> Integer -> [Integer]
findFactors counter num
| counter > div num 2 = []
| otherwise = if num `isAFactor` counter
then counter:findFactors (counter+1) num
else findFactors (counter + 1) num
时重新计算商数,即findFactors
个分区(尽管n/2
似乎意识到这一点并且只计算一次)。其次,到处处理这个计数器变量有点烦人。第三,它仍然非常必要。
另一种查看问题的方法是从ghc -O2
到1
获取整数列表,并过滤那些n/2
因子的整数。这直接转换为Haskell:
n
发现它具有与上述版本相同的性能特征可能会令人惊讶。 Haskell不需要立即为整个列表分配内存findFactors :: Integer -> [Integer]
findFactors num = filter (isAFactor num) [1..(num `div` 2)]
,它可以根据需要生成每个值。
答案 2 :(得分:8)
其他人在解释您的代码方面做得很好,所以让我帮您解码错误信息。
Couldn't match expected type `[a] -> [Integer]'
against inferred type `IO ()'
In the expression:
putStrLn ("factorList is : " ++ show quotient) []
我错过了所有其他“在表达式”中的部分;他们只是展示了越来越多的封闭背景。
Haskell中的所有内容都是一个表达式,因此所有内容都有一个类型。这包括像“putStrLn”这样的东西。如果你在GHCi中输入“:t putStrLn”,你会看到它回复:
putStrLn :: String -> IO ()
这意味着putStrLn是一个接受字符串并返回“IO动作”的函数,在这种情况下是将消息放在屏幕上的动作。在你的代码中,你给了“putStrLn”一个字符串,因此编译器推断表达式“putStrLn( stuff )”的类型为“IO()”。这是编译器错误消息的“推断类型”部分。
与此同时,编译器也在外部进行另一个方向的类型推断。除此之外,它注意到这个“putStrLn( stuff )”表达式似乎应用于空列表,其类型为“[a]”(即某个列表,我们不知道是什么)。此外,整个表达式的结果应该是“[Integer]”类型。因此,表达式“putStrLn( stuff )”应该是将“[]”变成整数列表的函数,其类型写为“[a] - > [整数]”。这是错误消息的“预期类型”部分。
此时编译器得出结论,它无法匹配这两种类型,因此报告了错误。
“无法匹配预期类型' Foo '与推断类型' Bar '”可能是您在尝试编译Haskell时获得的最常见错误消息,因此其值得尝试阅读并理解它。查看推断类型并尝试找出引用表达式的哪个部分具有该类型。然后通过查看周围的代码,尝试找出编译器为什么期望别的东西。
答案 3 :(得分:3)
更新了说明
问题是Haskell中的IO是monadic,以do
开头的块是用于将monadic表达式(有时称为语句)与monadic运算符组合在一起的语法糖。在这种情况下,有问题的monad是IO monad,可以从对putStrLn的调用中推断出来。 []
块的第二行中的do
实际上是而不是整个do块的值,而是被解释为putStrLn的最后一个参数;并不是说它接受第二个参数,但是编译器甚至没有达到解决这个问题的程度,因为它会在你引用的类型错误之前终止。要使该行成为命令,您必须在其前面放置另一个monadic函数(例如return []
)。不过,我不是说这可以帮助你解决问题。
类型错误源于IO monadic表达式始终具有类型IO _
;在您的情况下,do
块也具有此类型,这显然与您在签名中指定的类型[Integer]
不兼容。
一般来说,因为Haskell是一个带有monadic IO的纯函数式语言,所以一旦你进入IO monad,就没有办法了,它是有意义的。也就是说,如果一个函数有一个带有IO操作的do
块,它的签名必然包含IO _
类型,所有其他函数的签名也会包含这个函数,等等。(其他monad)确实提供“退出”功能,但IO monad没有。)