如何将整数转换为分数

8
假设我有以下Haskell类型描述:
divide_by_hundred :: Integer -> IO()
divide_by_hundred n = print(n/100)

为什么在我尝试用ghc运行这个程序时会出现以下信息:
No instance for (Fractional Integer) arising from a use of `/'
Possible fix: add an instance declaration for (Fractional Integer)
In the first argument of `print', namely `(n / 100)'
In the expression: print (n / 100)
In an equation for `divide_by_hundred':
    divide_by_hundred n = print (n / 100)

通过运行:t (/),我得到了以下结果:
(/) :: Fractional a => a -> a -> a

我认为这表明(/)可以接受任何可以表示为分数的数字(我认为这应该包括整数,但我不确定如何验证),只要/的两个输入类型相同。

显然这是不准确的。为什么呢?我该如何编写一个简单的函数来将整数除以100?


5
请养成将此类测试编写为“纯函数”,而不是在IO单子中进行的习惯。除了其他优点外,这使返回类型显式化(print会隐藏类型),从而提供更多控制权。此外,请注意camelCase是Haskeller们的惯例。因此,您应该确实将divideByHundred :: Integer -> Integer或者divideByHundred :: (Fractional f) => Integer -> f - leftaroundabout
2个回答

15
哈斯克尔喜欢保持运算符的数学意义。例如,/ 应该是乘法的倒数,但对于 Fractional Integer 实例,5 / 4 * 4 不可能得到 51
因此,如果您实际上想要进行截断整数除法,语言会强制您明确使用 divquot。另一方面,如果您实际上希望将结果作为分数呈现,可以使用 /,但您需要先将其转换为具有 Fractional 实例的类型。例如,
Prelude> let x = 5
Prelude> :t x
x :: Integer
Prelude> let y = fromIntegral x / 100
Prelude> y
5.0e-2
Prelude> :t y
y :: Double

请注意,这里 GHCi 选择了 Double 实例作为最简单的默认值;你还可以这样做:
Prelude> let y' = fromIntegral x / 100 :: Rational
Prelude> y'
1 % 20

1严格来说,这个反向恒等式对于Double实例也不完全成立,因为由于浮点数字的问题,它只是近似成立。

2实际上,这并不是语言本身的问题,而是标准库的问题。你可以定义

instance Fractional Integer where
  (/) = div

如果你只是想要翻译自己的代码,那么你原本的代码应该可以正常工作。但这样做是个坏主意!


6
您可以使用 div 进行整数除法:
div :: Integral a => a -> a -> a

或者您可以使用 fromIntegral 将整数转换为分数:

fromIntegral :: (Integral a, Num b) => a -> b

因此,本质上来说:
divide_by_hundred :: Integer -> IO()
divide_by_hundred n = print $ fromIntegral n / 100

整数类型不支持分数运算,具体可以在手册中查看。

如何在使用fromIntegral将其转换为小数后,将其恢复为字符串? - peni4142

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接