【问题标题】:Haskell: Can verbosity of converting from/to Integer be made more succinct?Haskell:从 Integer 转换为 Integer 的详细程度可以变得更简洁吗?
【发布时间】:2015-02-21 20:31:14
【问题描述】:

我在 Haskell 中有以下代码:

addm a b m = fromInteger $ mod (fromIntegral a + fromIntegral b) (fromIntegral m)

这个想法是,如果a、b 和m 的类型没有足够的位使得加法可以换行(例如,如果使用Word8),那么计算将不正确.因此,为了防止这种情况,我们暂时转换为Integer,进行计算,然后再转换回来。

如果没有三个fromIntegral 和一个fromInteger,有没有更简洁的写法?

【问题讨论】:

  • 可以缩短fromIntegrak的名字,可以做一个ho函数,封装所有的转换,...​​
  • 但是你真正想要什么?你能描述一下所有这些操作的目的吗?为什么在这种情况下溢出实际上很糟糕?

标签: haskell types type-conversion


【解决方案1】:

一方面,您应该使用toInteger 和fromInteger,因为fromIntegral 将转换为Num,而不是Integer。不幸的是,没有一种特别友好的方法可以做到这一点,但是使用一些组合器可以使其更短:

import Data.Function (on)

addm a b m = fromInteger $ flip mod (toInteger m) $ on (+) toInteger a b

但是有运营商

infixr 9 .:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)

您可以将其定义为(如果您将m 作为第一个参数)

addm m = fromInteger .: flip mod (toInteger m) .: on (+) toInteger

但是在这一点上,让它变得更无意义而不使其不可读有点困难。在 Haskell 中强制转换总是明确的,你无法绕过它。有些名字只是让它有点冗长,但你可以从中获得类型安全。就个人而言,我可能会改为定义此函数的两个版本,一个特定于Integer,另一个泛化为Integral:

addm :: Integral a => a -> a -> a -> a
addm a b m = fromInteger $ addmInteger (toInteger a) (toInteger b) (toInteger m)
    where
        addmInteger :: Integer -> Integer -> Integer -> Integer
        addmInteger a' b' m' = (a' + b') `mod` m'

虽然这是更多类型的输入,但它清晰地将逻辑与类型转换分开。您可以使用类似于on 的组合器来缩短它:

addm :: Integral a => a -> a -> a -> a
addm a b m = fromInteger $ on3 addmInteger toInteger a b m
    where
        on3 :: (b -> b -> b -> c) -> (a -> b) -> a -> a -> a -> c
        on3 f g x y z = f (g x) (g y) (g z)
        addmInteger :: Integer -> Integer -> Integer -> Integer
        addmInteger a' b' m' = (a' + b') `mod` m'

虽然这是更多的总代码。我个人还是更喜欢它,因为on3 可以很容易地在其他地方使用,并且业务逻辑仍然与强制分离。

【讨论】:

    【解决方案2】:

    扩展至augustuss's comment:

    addm a b m = i $ mod (i a + i b) (i m)
      where i = fromIntegral
    

    (fromInteger 和 fromIntegral 在 Integers 上的工作方式相同,因此我们可以将 fromInteger 替换为 fromIntegral)。

    【讨论】:

      【解决方案3】:

      你正在尝试定义

      addm a b m = fromInteger $
         (fromIntegral a + fromIntegral b) `mod` (fromIntegral m)
      

      但是你真正想要什么?

      根据描述,我将假设 inputs 与 output 的类型相同。我还将假设所涉及的类型表现得非常好——对于某些k,溢出很好地模数k。 Word8 (k = 2^8)、Int8 (k = 2^8) 等就是这种情况,在 GHC 中Word 和Int 也是这种情况。那么我在这里得到什么?

      对于一些q 和一些r 这样0<=r<m,

      a + b - q * m = r
      

      这是欧几里得除法的基本概念(divMod 有点奇怪,并没有完全实现,但非常接近)。但后来

      a' + b' - q' * m' ≡ r' (mod k)
      

      对于任何 a'、b'、q' 和 m' 与 a、b、q 和 m 模 k 一致.这强烈表明,一旦您明确了计算的实际目的,溢出就不会成为真正的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-05
        • 1970-01-01
        • 2021-02-21
        • 2013-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-02
        相关资源
        最近更新 更多