在Haskell中实现余弦定律?

Implement the law of cosine in Haskell?

所以我想用这个公式创建我自己的余弦函数:

只要绝对值大于0.001,函数就应该return这个值。

但是,我的代码似乎有一些我不知道如何修复的类型错误。我已经尝试将所有类型更改为 Double,但仍然无效。

fac :: Int -> Int
fac n = if (n == 0) then 1 else n * fac (n-1)

cos :: Double -> Double
cos x = sum [cos| k <- [0..],
                  let cos = (-1) * (x^(2*k) `div` fac (2*k)) , 
                  abs (cos) > 0.001]

这是错误:

• 无法将预期类型“Double”与实际类型“Int”相匹配

• 在‘div’的第二个参数中,即‘fac(2 * k)’

这里基本上有两个问题:

  • div 仅用于整数除法(即采用整数输入,并通过向下舍入产生整数输出)。您想要 (/) 进行浮点除法。
  • fac returns 和 Int,您必须在除法中使用之前显式转换为浮点数。 (许多语言会自动从整数类型转换为浮点类型,但 Haskell 不会。)您可以使用 fromIntegral 进行转换。

只解决这两件事,我们得到:

fac :: Int -> Int
fac n = if (n == 0) then 1 else n * fac (n-1)

cos :: Double -> Double
cos x = sum [cos| k <- [0..],
                  let cos = (-1) * (x^(2*k) / fromIntegral (fac (2*k))) , 
                  abs (cos) > 0.001]

这个类型检查,尽管它还有其他几个问题,按从最重要到最不重要的顺序排列:

  • 您可能希望 Haskell 会以某种方式神奇地知道一旦 abs cos <= 0.001 在一次迭代中,它会在所有未来的迭代中继续如此,并停止迭代。但事实并非如此。您说要从 [0..] 中抽取 k,因此它将从整个列表中抽取 k——永远不会完成。您可能更喜欢 takeWhile
  • 您在 -1 上丢失了指数 k
  • 即使不考虑这是否是最有效的入手公式,您的实施也会重复很多工作。对 fac (2*k) 的每次调用都必须重新计算它之前完成的所有阶乘作为中间结果;并且您的 x^(2*k) 重复了一些工作(尽管程度要小得多)。
  • 重复使用名称 cos 非常令人困惑。虽然从技术上讲它不会使程序 错误 ,但我会避免它。

我留给你探索 Haskell 和学习如何自己解决这些问题的乐趣 -- 我认为这完全在你在这里展示的能力范围内!