【问题标题】:Adding different number types in Haskell在 Haskell 中添加不同的数字类型
【发布时间】:2016-11-02 01:31:04
【问题描述】:

我使用 Haskell 已经有一段时间了,但在使用数值类型时遇到了一些问题。大多数时候我可以在尝试一下之后解决,但这次我被这段微不足道的代码难倒了两个多小时。

我有这些功能:

takeLessEqual x = takeWhile (<=x)

leftHalf x = takeLessEqual x $ (map (\x -> ((x+0.5)*(x+0.5) + 0.75))) [1..]
-- Produces the list [3.0,7.0,13.0,21.0,31.0,43.0 ... (some number < x)]

rightHalf x = takeLessEqual x $ (map (\x -> if even x then x*x + 1 else x*x)) [1..]
-- Produces the list [1,5,9,17,25,37,49 ... (some number < x)]   

total x = (sum $ rightHalf x) + (sum $ leftHalf x)
-- total 10 Should produce some number 25 or 25.0

它加载到 ghci 时没有错误,但是当我尝试评估时:

*> leftHalf 10 
[3.0,7.0]
it :: (Ord a, Fractional a, Enum a) => [a]

*> rightHalf 10
[1,5,9]
it :: Integral a => [a]

*> total 10

<interactive>:150:1: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘it’
  prevents the constraint ‘(Fractional a0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘a0’ should be.
  These potential instances exist:
    instance Fractional Double -- Defined in ‘GHC.Float’
    instance Fractional Float -- Defined in ‘GHC.Float’
    ...plus one instance involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the first argument of ‘print’, namely ‘it’
  In a stmt of an interactive GHCi command: print it

我已经尝试在不同点添加类型注释并使用 toInteger 和 fromIntegral 转换类型,但没有成功。

我做错了什么,我该如何解决?

【问题讨论】:

  • 试试:total 10 :: Double
  • 我已经尝试过这个 ofc --> • 没有使用 'total' 产生 (Integral Double) 的实例 • 在表达式中:total 10 :: Double 在 'it' 的等式中:它 = 总共 10 :: 双倍
  • 是的,问题是Haskell没有任何类型同时是IntegralFractional,但是你的total函数需要这样的类型!
  • fwiw,您可以将leftHalf 重写为leftHalf x = takeLessEqual x $ map (\x -&gt; (x * x + x + 1) [1..],它适用于所有数字类型,而不仅仅是IntegralFractional 类型。

标签: haskell


【解决方案1】:

首先,我想建议您绝对遵循 GHC 的添加类型签名的建议。我建议始终为顶级定义提供类型签名。这将使错误消息明显更加清晰。

如果我们继续这样做,假设您想要的 Fractional 类型是 Double 并且 Integral 类型是 Int,我们有

takeLessEqual :: Ord a => a -> [a] -> [a]
takeLessEqual x = takeWhile (<=x)

leftHalf :: Double -> [Double] -- This must be some `Fractional` type since we are doing things like adding by 0.5
leftHalf x = takeLessEqual x $ (map (\x -> ((x+0.5)*(x+0.5) + 0.75))) [1..]

rightHalf :: Int -> [Int]      -- This must be some `Integral` type since we are using `even`
rightHalf x = takeLessEqual x $ (map (\x -> if even x then x*x + 1 else x*x)) [1..]

-- total :: ?
total x = (sum $ rightHalf x) + (sum $ leftHalf x)

请注意,IntegralFractional 都不是类型,所以我们不能在这里到处使用相同的数字类型。

现在的问题是total+的两侧不是同一种类型,即使它们必须是((+) :: Num a =&gt; a -&gt; a -&gt; a)。

现在,您需要决定是要最终使用 Doubles 还是 Ints(或组合。我假设您希望 Double 用于输入和输出):

total :: Double -> Double

现在,我们看到rightHalf 需要Int,因此我们必须使用ceilingfloorround 将参数从Double 转换。我们得到的结果是一个[Int] 输入到sum,这给了我们一个Int(因为sum :: Num a =&gt; [a] -&gt; a)。

假设ceiling 给出了我们想要的舍入,我们最终得到:

total x = (fromIntegral . sum . rightHalf $ ceiling x) + (sum $ leftHalf x)

在文体方面,我还建议写 ... + sum (leftHalf x) 而不是 ... + (sum $ leftHalf x)

一般来说,如果您想从小数类型转换为整数类型,您将需要使用floorceilinground。如果您想从整数类型转换为其他类型(通常是小数类型,但也可能是一些不同的整数类型),您需要fromIntegral

您可以在不同的位置(leftHalf 和/或rightHalf)进行转换。不过,这两个函数似乎应该分别保持浮动和整数,因为您必须选择一个舍入方法(当调用这两个函数的函数应该能够决定舍入方法时)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 2014-06-03
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多