计算哈斯克尔的窦

时间:2014-10-26 18:15:40

标签: c++ haskell

这是我的问题:我需要一个计算某些数字的窦的函数......

在C ++中我写了这个:

double msin(double number, int counter = 0, double sum = 0)
{
    // sin(x) = x - (x'3 / 3!) + (x'5 / 5!) - (x'7 / 7!) + (x'9 / 9!)
    if (counter <= 20)
    {
        if (counter % 2 == 0)
            sum += mpow(number, counter * 2 + 1) / mfak(counter * 2 + 1) ;
        else
            sum -= mpow(number, counter * 2 + 1) / mfak(counter * 2 + 1) ;

        counter++;
        sum =  msin(number, counter, sum);

        return sum;
    }

    return (sum* 180.0 / _PI);
}

现在我试图在Haskell中做到这一点,我不知道如何......现在我正在尝试这样的事情(它确实没有用,但正在进行中;)):

这有效:

mfak number = if number < 2
                    then 1
                    else number *( mfak (number -1 )) 


mpow number potenca = if potenca == 0
                        then 0
                        else if potenca == 1
                        then 1
                        else (number * (mpow number (potenca-1)))

这不起作用:

msin :: Double -> Int -> Double -> Double                           
msin number counter sum = if counter <= 20
                                    then if counter `mod` 2==0
                                            then let sum = sum + (msin 1 (let counter = counter+1 in counter) sum) in sum 
                                            else let sum = sum + (msin 1 (let counter = counter+1 in counter) sum) in sum                                   
                                    else sum* 180.0 / 3.14

更新....没有编译:/&#34;无法匹配预期类型Double' with actual type Int&#39;&#34;

msin :: Double -> Int -> Double -> Double                           
msin number counter sum = if counter <= 20
                                    then if counter `mod` 2==0
                                            then let sum' = sum + ((mpow number (counter*2+1))/(mfak counter*2+1)) in msin number (counter+1) sum'
                                            else let sum' = sum - ((mpow number (counter*2+1))/(mfak counter*2+1)) in msin number (counter+1) sum'                              
                                    else sum* 180.0 / 3.14

正如你所看到的,最大的问题是如何在&#34;总和&#34;中添加内容,增加&#34;计数器&#34;并使用这些新值再次进行递归......

P.S。我是Haskell的新手,所以尽可能多地尝试解决你的解决方案。我正在阅读一些教程,但是,我无法找到如何将某些表达式的结果保存到一个值中,然后继续使用其他代码......它只是在每次我尝试这样做时返回我的值,而且我不想那样....

提前tnxx提供任何帮助!

5 个答案:

答案 0 :(得分:5)

问题是像let stevec = stevec+1 in stevec这样的表达式。 Haskell不是一种命令式语言。这不会向stevec添加一个。相反,它将stevec定义为比自身多一个的数字。没有这样的数字,因此你会得到一个无限循环,或者,如果你幸运的话,你会崩溃。

而不是

stevec++;
vsota =  msin(stevilo, stevec, vsota);

您应该使用类似

的内容
let stevec' = stevec + 1
in  msin stevilo stevec' vsota

或只是

msin stevilo (stevec + 1) vsota

(这里还有一些我不明白的东西。你需要mpowmfak。他们在哪里?)

答案 1 :(得分:4)

我会稍微修改算法。首先,我们可以定义阶乘反演列表:

factorialInv :: [Double]
factorialInv = scanl (/) 1 [1..]  -- 1/0! , 1/1! , 1/2! , 1/3! , ...

然后,我们遵循正弦系数:

sineCoefficients :: [Double]
sineCoefficients = 0 : 1 : 0 : -1 : sineCoefficients

然后,在给定x的情况下,我们将上述列表与x的幂相乘,逐点:

powerSeries :: [Double]   -- ^ Coefficients
            -> Double     -- ^ Point x on which to compute the series
            -> [Double]   -- ^ Series terms
powerSeries cs x = zipWith3 (\a b c -> a * b * c) cs powers factorialInv
   where powers = iterate (*x) 1   -- 1 , x , x^2 , x^3 , ...

最后,我们采用前20个术语并总结。

sine :: Double -> Double                   
sine = sum . take 20 . powerSeries sineCoefficients
 -- i.e., sine x = sum (take 20 (powerSeries sineCoefficients x))

答案 2 :(得分:1)

As you can see the biggest problem is how to add something to "vsota",

在函数式语言中,您将在此处使用递归 - 变量vstota实现为函数参数,在处理列表时将其从调用传递给调用。

例如,要总结一个数字列表,我们会写一些类似的东西:

sum xs = go 0 xs
  where go total [] = total
        go total (x:xs) = go (total+x) xs

在命令式语言中total将是一个更新的变量。这是一个函数参数,它被传递给下一个递归调用go

在你的情况下,我会首先编写一个函数来生成幂级数的术语:

sinusTerms n x = ... -- the first n terms of x - (x'3 / 3!) + (x'5 / 5!) - (x'7 / 7!) ...

然后使用上面的sum函数:

sinus n x = sum (sinusTerms n x)

答案 3 :(得分:1)

您还可以使用递归列表定义来获取[x, x^3, x^5 ...][1, 1/3!, 1/5! ...]无限序列。当它们完成后,剩下的就是将它们各自的项目相乘并取总和。

sinus count x = sum (take count $ zipWith (*) ifactorials xpowers)
    where xpowers     = x : map ((x*x)*) xpowers 
          ifactorials = 1 : zipWith (/) ifactorials [i*(i+1) | i <- [2, 4 .. ]]

此外,定义xpowers = iterate ((x*x)*) x会更好,因为它似乎更具可读性。

答案 4 :(得分:0)

我尽可能地遵循你的惯例。对于mfakmpow,您应该避免使用if,因为使用模式匹配更清楚地编写它们:

mfak :: Int -> Int
mfak 0 = 1
mfak 1 = 1
mfak n = n * mfak (n - 1)

mpow :: Double -> Int -> Double
mpow _ 0 = 1
mpow x 1 = x
mpow x p = x * mpow x (p - 1)

在计算窦之前,我们创建一系列系数[(sign, power, factorial)]

x - (x^3 / 3!) + (x^5 / 5!) - (x^7 / 7!) + (x^9 / 9!)
→ [(1,1,1), (-1,3,6), (1,5,120), (-1,7,5040), (1,9,362880)]

列表由列表理解创建。首先,我们将列表[1,-1,1,-1,1,-1...][1,3,5,7,9,11...]压缩。这为我们提供了列表[(1,1), (-1,3), (1,5), (-1,7)...]。从此列表中,我们创建了最终列表[(1,1,1), (-1,3,6), (1,5,120), (-1,7,5040)...]

sinCoeff :: [(Double, Int, Double)]
sinCoeff = [ (fromIntegral s, i, fromIntegral $ mfak i)
           | (s, i) <- zip (cycle [1, -1]) [1,3..]]

cycle无限期地重复列表,[1,3..]创建一个无限列表,从1开始,步长为2)

最后,msin函数接近定义。它还使用列表推导来实现它的goeal(注意我保留了* 180 / pi虽然我不确定它应该在那里.Haskell知道pi。)

msin :: Int -> Double -> Double
msin n x = 180 * sum [ s * mpow x p / f | (s, p, f) <- take n sinCoeff] / pi

take n sinCoeff返回列表的第一个n元素)

您可以尝试使用以下代码:

main = do
    print $ take 10 sinCoeff
    print $ msin 5 0.5
    print $ msin 10 0.5