【发布时间】: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",因为Preludetake函数使用固定长度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