2013-03-01 61 views
1

这是一段非常简单的代码。它接受一个整数并使用商和余数来分解其小数位。在每次调用时,它会打印一个包含r个字符'I'的行,其中r是最后一位数字,然后使用商作为新参数调用自身。具有10位或更多位数的乱码转换

decToUnary 0 = return() 
decToUnary n = let (q, r) = quotRem n 10 in 
    do 
     putStrLn (take r "IIIIIIIIII") 
     decToUnary q 

它适用于小于10位数的数字,但是对于10位或更多位数,它会对输出进行加扰。我做错了什么,为什么它以这种方式工作? 这里是输出的一些实例中,第一个是正确的,所述第二是错误的:

*Main> decToUnary 5432 
II 
III 
IIII 
IIIII 

*Main> decToUnary 54321
IIIIIIII 
III 
IIIIIIIII 
III 
III 
I 
IIIIIII 
III 
I 
I 
+4

保理注意事项:'decToUnary'实际上应该是一个纯粹的'Integer - > [String]'函数。然后你可以用'mapM_ putStrLn(decToUnary whatever)'打印这个列表。 – 2013-03-01 19:53:46

+0

'enrique'请注意,在采用'dave4420'的建议并将签名交替到'Integer - > IO()'时,您需要将'take r“IIIIIIIIII''行改为'take(fromInteger r)”IIIIIIIIII “'因为Prelude'take'函数使用固定长度'Int' – applicative 2013-03-01 19:59:15

+0

注意:使用'Integer',不能直接使用余数'r'作为'take'的参数。你可以使用'take(fromInteger r)“III ...”'或者'import Data.List'并使用'genericTake'。 – 2013-03-01 19:59:26

回答

13

这是一个整数溢出问题。 maxBound :: Int是2147483647(在32位机器上),因此值大于溢出。

使用Integer而不是IntInteger不是一个固定长度的整数,所以它不会溢出。

编辑:作为应用性的笔记,然后你会需要更换take r "IIIIIIIIII"take (fromIntegral r) "IIIIIIIIII"genericTake r "IIIIIIIIII";我更喜欢genericReplicate r 'I'

genericTakegenericReplicate都在Data.List中。

+1

这些类型的问题最近的尖峰使我认为Cryptol和习惯是正确的 - 不要让人们声称文字有一个类型,意味着他们{over,under}流。一个新的Haskell Prime提议的时间! – 2013-03-01 21:16:34

+0

好吧,现在我明白了。问题在于'quotRem'的类型签名。 'take'的参数总是小于10(不是溢出问题),但是'quotRem'为两个输入和两个输出分配相同的类型:'quotRem :: Integral a => a - > a - >( a,a)'。如果它是'Rem ::(Integral a1,Integral a2)=> a1 - > a2 - >(a1,a2)',它将避免问题,因为整数除法的其余部分总是小于除数,商数将少于股息('divMod'也是如此)。 – enrique 2013-03-03 03:03:03