我目前正致力于Project Euler的乐趣,并使用Haskell进行练习。但是,我遇到了一个问题,我似乎无法生成一个更小的例子,所以这里是代码(对于项目euler 232):
buildNum (x, y) = multNum x y 1 []
where multNum num mul exp s = if (num > 10 ^ 100) then s else multNum nNum mul nexp ns
where next = (num * mul) `div` exp
ns = num:s
top = next `mod` 10
nexp = exp * 10
nNum = num + nexp * top
sumBuild (x, y) = (head (buildNum (x, y))) * length (buildNum (x, y))
请忽略不良风格:)
如果我加载它并在这里运行sumBuild(7,5),我会得到一个例外:
*** Exception: Prelude.head: empty list
但如果我将sumBuild更改为:
sumBuild (x, y) = head (buildNum (x, y))
或
sumBuild (x, y) = length (buildNum (x, y))
然后运行正常。
这对我来说真的很混乱,因为没有副作用,两次跑步产生不同的结果(至少看起来是这样的情况)。这有什么问题?
如果有人可以将这个程序编辑成一个最小的工作示例,那么我真的很感激!
感谢您的帮助!
答案 0 :(得分:7)
这是提示:
> :t sumBuild
sumBuild :: (Int, Int) -> Int
> let a = 7 :: Int
> a > 10^100
True
作为建议,请始终明确声明顶级声明的类型。这有助于避免此类错误。
答案 1 :(得分:6)
问题在于,通过乘以length
的结果Int
,您可以强制使用机器大小的Int
类型完成所有计算,从而导致溢出。删除时,将使用默认的任意大小Integer
类型。
如果添加类型签名和fromIntegral
以强制使用Integer
完成计算,则一切都按预期工作。
buildNum :: (Integer, Integer) -> [Integer]
buildNum (x, y) = -- same as before
sumBuild :: (Integer, Integer) -> Integer
sumBuild (x, y) = (head (buildNum (x, y))) * fromIntegral (length (buildNum (x, y)))