2013-03-01 54 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