我仍然不理解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] ]
没有运气。
我在这里缺少什么?
答案 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
类的一部分。这意味着您的结果必须同时为Integral
和Fractional
,我认为这种类型不存在。
解决方案是在将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]
。我认为这并不重要,否则修复是微不足道的。
最后:我假设你知道这个公式在收敛速度方面非常糟糕!