【问题标题】:Scrambled number conversion for numbers with 10 or more digits10位或更多位数字的加扰数字转换
【发布时间】:2013-03-01 19:27:12
【问题描述】:

这是一段非常简单的代码。它接受一个整数并使用商和余数分解其十进制数字。在每次调用时,它打印一行包含 r 个字符“I”,其中 r 是最后一个数字,然后以商作为其新参数调用自身。

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 5432101234
IIIIIIII
III
IIIIIIIII
III
III
I
IIIIIII
III
I
I

【问题讨论】:

  • 考虑因素:decToUnary 应该是一个纯 Integer -> [String] 函数。然后您可以使用mapM_ putStrLn (decToUnary whatever) 打印列表。
  • enrique 请注意,在接受dave4420 的建议并将签名更改为Integer -> IO () 时,您需要将行take r "IIIIIIIIII" 更改为take (fromInteger r) "IIIIIIIIII",因为Prelude take 函数使用固定长度Int
  • 注意:对于Integer,你不能直接使用余数r作为take的参数。您可以使用 take (fromInteger r) "III..." then 或 import Data.List 并使用 genericTake
  • 谢谢大家。事实上,签名 decToUnary :: Int -> IO() 是编译器选择的。我认为编译器不必决定类型优化,而是为表达式计算最通用和最安全的类型,因为语言设计的其余部分中的一个重要标准是避免副作用。 @thomas,这是我的 Haskell Prime 提案。
  • @enrique 是的,因为您使用了take。它需要一个Int,并且该知识被传播回您的函数类型。您可以使用fromIntegral r 而不仅仅是r 来隔离它。然后编译器会将Int 类型分配给fromIntegral r 的结果。这只会将r 的类型限制为Integral 类型类。试试吧,用这样改变的代码和没有显式类型签名。

标签: haskell


【解决方案1】:

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

使用Integer 代替IntInteger 不是固定长度整数,因此不会溢出。

编辑:作为应用说明,您需要将take r "IIIIIIIIII" 替换为take (fromIntegral r) "IIIIIIIIII"genericTake r "IIIIIIIIII";我更喜欢genericReplicate r 'I'

genericTakegenericReplicate 都在 Data.List 中。

【讨论】:

  • 最近这类问题的激增让我认为 Cryptol 和 Habit 是正确的——不要让人们声称字面量的类型意味着他们{over,under}flow。是时候提出新的 Haskell Prime 提案了!
  • 好的,现在我明白了。问题出在'quotRem'的类型签名上。 take 的参数将始终小于 10(不是溢出问题),但 quotRem 为两个输入和两个输出分配相同的类型:quotRem :: Integral a => a -> a -> (a, a)。如果它是quotRem :: (Integral a1, Integral a2) => a1 -> a2 -> (a1, a2),它将避免问题,因为整数除法的其余部分总是小于除数,商将小于被除数(divMod 也是如此)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-31
  • 1970-01-01
  • 2012-07-26
  • 1970-01-01
  • 1970-01-01
  • 2022-12-09
  • 1970-01-01
相关资源
最近更新 更多