Haskell-如何在递归函数中跟踪计数器

时间:2015-01-16 23:49:28

标签: haskell recursion

我在尝试跟踪我的函数中的计数器变量时遇到了一些困难。

我创建了一个函数,它将一个数字作为单个参数,并递归地将该数字乘以2,将所有数字相乘,将两个数字相乘,下面的代码使我更清楚我打算做什么:

sum :: Float -> Float
sum x = x + sum (2*x)

然而,我面临的困难是,我希望这个功能只能递归十次。所以我希望一旦添加十个数字就停止它。我尝试使用计数器来跟踪函数递归的次数,但无济于事。

这就是我的尝试:

sumTen :: Float -> Float
sumTen x n
    where
      n=10
       |n == 0 = x
       |otherwise = x + sumTen (2*x) (n-1)

我意识到上面的代码不起作用,计数器n将在每次递归调用时被赋予值10,这意味着它永远不会到达基本情况n == 0

这使得如此困难的原因是必须使用一个参数调用sumTen。在上面的代码中,我尝试为函数提供一个具有预定值的默认参数n,但它显然不起作用。

有人可以帮助我在递归函数后停止使用' n'递归调用?

4 个答案:

答案 0 :(得分:5)

您可以使用辅助功能:

sumNRec x 0     = 0 
sumNRec x times = x + sumNRec (x*2) (times - 1)

sumTen x = sumNRec x 10

答案 1 :(得分:3)

让我们以一般方式解决问题。从原始功能开始:

sum :: Float -> Float
sum x = x + sum (2*x)

首先,添加一个额外的rec参数。这个rec代表“递归调用”,它是我们要调用的函数,而不是函数体中的所有递归调用。具体而言,只需使用sum替换定义右侧显示的任何rec

sumK :: (Float -> Float) -> Float -> Float
sumK rec x = x + rec (2*x)

现在,如果我们想要递归函数执行零次,我们将获得id。要只递归一次,我们可以使用sumK id,因为

sumK id x = x + id (2*x) = x + 2*x

要递归两次:sumK (sumK id),因为

sumK (sumK id) x = x + sumK id (2*x) = x + (2*x) + 2*(2*x)

依此类推:要递归n次,我们只需运行sumK (... (sumK id)...) sumK nsumK . sumK . ... . sumK $ id次。同样,这可以写成组合[sumK,sumK,...]。因此,要获得生成列表id就足够了,请撰写它们,并在最后应用sum10 :: Float -> Float sum10 = compose (replicate 10 sumK) id compose :: [a -> a] -> a -> a compose = foldr (.) id

recurseOnly :: Int -> ((a->a)->(a->a)) -> (a->a)
recurseOnly n funK = compose (replicate n funK) id

sum10 :: Float -> Float
sum10 = recurseOnly 10 sumK

如果您愿意,可以写一个小的一般助手:

{{1}}

答案 2 :(得分:2)

sumTen :: Float -> Float
sumTen x = go 10 x
    where
      go n x
       | n == 0 = x
       | otherwise = x + go (n-1) (2*x)

这里有两个要点:go是两个参数的简单递归函数,它传递n以使您在正确的时间停止。但是因为您不想公开该参数,所以顶级sumTen函数只是go的部分应用版本。你甚至可以把它写成:

sumTen = go 10
  where go n x = -- ...

你喜欢哪一个是风格选择,真的。

答案 3 :(得分:1)

也许这就是:

sumTen x = sum $ take (10+1) $ iterate (* 2) x