计算" e"不断使用Haskell直到功能

时间:2016-01-01 17:34:10

标签: haskell

我想计算" e"不断使用Haskell(Prelude)内置直到函数。我想做这样的事情:

enumber = until (>2.7) iter (1 0)

iter x k = x + (1/(fact (k + 1)))

fact k = foldr (*) 1 [1..k]

当我尝试运行此代码时,出现此错误:

Occurs check: cannot construct the infinite type: a ~ a -> a
Expected type: (a -> a) -> a -> a
  Actual type: a -> a -> a
Relevant bindings include enumber :: a -> a (bound at Lab2.hs:65:1)
In the second argument of ‘until’, namely ‘iter’
In the expression: until (> 2.7) iter (1 0)

通过" e"我的意思是e = 2.71828 ..

2 个答案:

答案 0 :(得分:3)

导致此错误的具体错误是符号(1 0)。这在Haskell中没有任何意义,它被解析为1是一个应用于0的函数,然后使用结果。你显然意味着将1和0作为(初始)参数传递。这就是我们所拥有的元组,写成(1,0)

现在,在尝试进行任何定义之前,我们应该明确我们需要的 types 并将其写出来。始终从您的类型签名开始,它们会引导您了解实际定义应该看到的内容!

enumber :: Double -- could also be a polymorphic number type, but let's keep it simple.

type Index = Double -- this should, perhaps, actually be an integer, but again for simlicity use only `Double`

fact :: Index -> Double

现在,如果您想执行enumber = until (>2.7) iter (1,0)之类的操作,那么iter需要同时添加系列扩展,并增加k索引(until知道没有关于指数),即像

这样的东西
iter :: (Double, Index) -> (Double, Index)

但是现在你的iter的签名更像是

iter :: Double -> Index -> Double

即。它没有做索引递增。此外,它是咖喱,即不接受作为元组的论据。

让我们尝试使用元组签名:

iter :: (Double, Index) -> (Double, Index)
iter (x,k) = ( x + 1/(fact (k + 1)), k+1 )

如果您想在until中使用此功能,则您遇到的问题是您始终使用元组,而不仅仅是累积的结果。你需要在终止条件和最终结果中丢弃索引:这可以通过fst函数轻松完成

enumber = fst $ until ((>2.7) . fst) iter (1,0)

现在,虽然这个版本的代码将进行类型检查,但它既不优雅也不高效也不准确(大于2.7在这里几乎不是一个有意义的条件......)。正如Chi所说,总结一些东西的好方法是scanl函数。

除了避免手动递增和传递索引外,还应该避免反复计算整个阶乘。这样做是非常通用的代码气味(这是fact未在标准库中定义的原因)

recipFacts :: [Double] -- Infinite list of reciprocal factorials, starting from 1/0!
recipFacts = go 1
 where go k = 1 : map (/k) (go (k+1))

顺便提一下,这也可以写成扫描:scanl (/) 1 [1..](由Will Ness提供)。

接下来,我们可以使用scanl来计算部分和,并使用一些终止条件。但是,因为这个系列收敛得如此之快,实际上是一个工作正常但更简单的黑客:

enumber :: Double
enumber = sum $ takeWhile (>0) recipFacts
           -- result: 2.7182818284590455

在这里,我使用了快速增长的阶乘快速导致浮点倒数下溢为零的事实。

当然,真的在这里根本不需要总结任何东西:最重要的定义是

enumber = exp 1

,别无其他。

答案 1 :(得分:2)

enumber = until (>2.7) iter (1 0)
                  --        ^^^^^

上面你将“function”1应用于参数0。这不起作用。

您可能希望使用一对(1, 0)。在这种情况下,不能将iter更改为接受并返回一对。此外,谓词>2.7必须适应成对。

如果您不想使用配对,则需要采用不同的方法。查找scanl函数,您可以使用该函数计算部分和。然后,您可以使用dropWhile来丢弃部分和,直到满足一些足够好的谓词。

一个例子:n^2的前十个部分和。

> take 10 $ scanl (+) 0 [ n^2 | n<-[1..] ]
[0,1,5,14,30,55,91,140,204,285]

请注意,此方法仅在您独立计算所有列表元素时才有效。如果要将一些计算值从一个元素重用到另一个元素,则还需要其他元素。 E.g。

> take 10 $ snd $ mapAccumL (\(s,p) x -> ((s+p,p*2),s+p)) (0,1) [1..]
[1,3,7,15,31,63,127,255,511,1023]

解剖:

mapAccumL (\(s,p) x -> ((s+p,p*2),s+p)) (0,1) [1..]
                         a   b    c      d e

s previous sum
p previous power of two
x current element of [1..]
a next sum
b next power of two
c element in the generated list
d first sum
e first power of two

尽管如此,我还不是mapAccumL的忠实粉丝。使用iterate和对看起来更好。