【问题标题】:How to return an Integral in Haskell?如何在 Haskell 中返回积分?
【发布时间】:2015-11-14 07:17:13
【问题描述】:

我正在尝试找出 Haskell,但我对“积分”有点坚持。 据我所知,Int 和 Integer 都是 Integral。 但是,如果我尝试编译这样的函数:

lastNums :: Integral a => a -> a
lastNums a = read ( tail ( show a ) ) :: Integer

我明白了

Could not deduce (a ~ Integer)
from the context (Integral a)

如何返回积分?

还可以说我必须坚持那个函数签名。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    让我们用英文读一下这个函数类型签名。

    lastNums :: Integral a => a -> a
    

    这意味着“让调用者选择任何整数类型。lastNums 函数可以获取该类型的值并产生另一个相同类型的值。”

    但是,您的定义总是返回 Integer。根据类型签名,应该将该决定留给调用者。

    解决此问题的最简单方法:

    lastNums :: Integer -> Integer
    lastNums = read . tail . show
    

    定义单态函数并不丢人。不要仅仅因为它可以是多态的就认为它必须是多态的。通常多态版本更复杂。

    这是另一种方式:

    lastNums :: (Integral a, Num a) => a -> a
    lastNums = fromInteger . read . tail . show . toInteger
    

    还有一种方式:

    lastNums :: (Integral a, Read a, Show a) => a -> a
    lastNums = read . tail . show
    

    【讨论】:

    • 另一种阅读英文签名的方式,与此答案相同,但重点略有不同:对于任何Integral 类型a 调用者选择,这函数接受并返回完全相同的类型。这种“呼叫者选择”动态对于理解这种情况非常有用。那么 OP 函数的问题是调用者可能选择了其他一些不是 IntegerIntegral 类型。
    • @dfeuer: fromIntegerNum 的一部分,上次我检查过。
    • @LuisCasillas:这是一种很好的表达方式。我已将其整合到答案中。
    • 是的,但IntegralReal 的子类(它本身是NumOrd 的子类)以及Enum
    【解决方案2】:

    虽然IntInteger 都实现了Integral,但Haskell 并不能那样工作。相反,如果您的函数返回 Integral a => a 类型的值,那么它必须能够返回实现 Integral 类型类的任何值。这与大多数 OOP 语言使用接口的方式不同,在后者中,您可以通过将接口的特定实例强制转换为接口类型来返回它。

    在这种情况下,如果您希望函数 lastNums 获取 Integral 值,将其转换为字符串,删除第一个数字,然后转换回 Integral 值,您必须实现它作为

    lastNums :: (Integral a, Show a, Read a) => a -> a
    lastNums a = read ( tail ( show a ) )
    

    【讨论】:

      【解决方案3】:

      您还需要能够阅读和展示。并摆脱 Integer 注释。 Integer 是具体类型,而 Integral 是类型类。

      lastNums :: (Integral a, Show a, Integral b, Read b) => a -> b
      lastNums = read . tail . show 
      
      *Main> lastNums (32 :: Int) :: Integer
      2
      

      【讨论】:

        【解决方案4】:

        Integral 类提供整数除法,它是Ord 的子类,所以它也有比较。因此我们可以跳过字符串,只做数学。警告:我尚未对此进行测试。

        lastNums x | x < 0 = -x
                   | otherwise = dropBiggest x
        
        dropBiggest x = db x 0 1
        
        db x acc !val
          | x < 10 = acc
          | otherwise = case x `quotRem` 10 of
                          (q, r) -> db q (acc + r * val) (val * 10)
        

        旁注:爆炸模式用于使dbval 中无条件严格。我们也可以在acc 中添加一个,但 GHC 几乎肯定会自己解决这个问题。最后我检查了一下,GHC 的本机代码生成器(默认后端)在优化已知除数的除法方面并不是那么出色。 LLVM 后端在这方面要好得多。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多