在Haskell的分部,仍然没有得到它

时间:2014-01-04 16:22:22

标签: haskell types integer int division

我仍然不理解Haskell中的划分。我的第一个目的是定义这样的功能:

piApprox :: (Integral a, Fractional b) => a -> b
piApprox n = 4 * sum [ (-1)^k / (2*k + 1) | k <- [0..n] ]

它不起作用。然后,使用签名:

piApprox :: (Fractional a) => Int -> a

但它再次引发了“无法推断”错误。

如果我在解释器中运行此代码以找出最佳签名,结果是:

Prelude> let piApprox n = 4 * sum [ (-1)^k / (2*k + 1) | k <- [0..n] ]
Prelude> :t piApprox
piApprox :: (Fractional a, Integral a) => a -> a

引发“类型变量`a0'是模糊的”错误。

目前,进行此计算的唯一方法是我可以考虑使用Ratio包,然后使用double转换为fromRational

import Data.Ratio
piApprox n = (fromRational) $ 4 * sum [ (-1)^k % (2*k + 1) | k <- [0..n] ]

它有效,但我不认为这是最好的方法。

我还认为即使输入和输出类型在签名中也是正确的,中间操作(-1)^k / (2*k + 1) - 放置分区 - 可能是问题,所以我也定义了:

piApprox' :: (Fractional a) => Int -> a
piApprox' n = 4 * sum [ (fromIntegral) $ (-1)^k / (2*k + 1) | k <- [0..n] ]
没有运气。 我在这里缺少什么?

3 个答案:

答案 0 :(得分:5)

这应该有效:

piApprox n = 4 * sum [ fromIntegral ((-1)^k) / fromIntegral (2*k + 1) | k <- [0..n] ]

fromIntegral函数的类型签名为:

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

所以它基本上将你的Integral类型转换为Num类型。

(/)的类型是:

Fractional a => a -> a -> a,因此您必须向其提供分数数据。

fromIntegral功能将通过将其转换为包含Num类型的Fractional类型来实现此目的。

答案 1 :(得分:2)

您的问题是您正在混合不兼容的数字类型。你说n是一些Integral(特别是在这种情况下是整数)。 k <- [0..n]表示k是相同的Integral类型。然后使用/除法,它是Fractional类的一部分。这意味着您的结果必须同时为IntegralFractional,我认为这种类型不存在。

解决方案是在将k与fromIntegral k一起使用之前将k转换为结果类型。

答案 2 :(得分:0)

我主张签名:

piApprox :: (Fractional r) => Int -> r

原因是,“precision”参数没有任何特定的“值”含义,它只是你愿意让这个函数运行的步数的计数器。 (更好的方法可能是将偏差指定为您想要允许的真实值π而不是评估深度,但这更复杂。)

接下来,当前实现冲突的点实际上是(-1)^k(它需要Integral,因为取幂是通过递归乘法实现的)。是的,这是表示数学和科学交替标志的通常方式,但如果你考虑它,这是一个非常糟糕的方式。你对这里的权力并不感兴趣,只是在符号交替中,而cycle [1, -1]更自然地实现了这一点。

对于乘法它是不同的,根本不需要Integral但要求两个参数都具有相同的类型。实现这一目标的自然方法是,立即使用Fractional变量,而不是从整数变换!因此,您可以使用[0..n]而不是[0 .. fromIntegral n]。每一步只需一次转换,而不是一次

实际上,最好不要绑定指数!由于这是Haskell,因此您可以将列表定义为无限(就像cycle也一样)。当然,你不能总结一个无限的列表,但你可以在这之前简单地修剪它:

piApprox :: (Fractional r) => Int -> r
piApprox n = 4 * sum (take n [ σ / (2*k + 1) | (σ,k) <- zip (cycle [-1,1]) [0..] ])

或者,或许用ParallelListComprehensions extension写得更好:

piApprox n = (4 *) . sum $
    take n [ σ / (2*k + 1) | σ <- cycle [1, -1]
                           | k <- [0..]
           ]

这比您的实施少一步,因为take n [0..]等同于[0..n-1]。我认为这并不重要,否则修复是微不足道的。


最后:我假设你知道这个公式在收敛速度方面非常糟糕!