我在尝试跟踪我的函数中的计数器变量时遇到了一些困难。
我创建了一个函数,它将一个数字作为单个参数,并递归地将该数字乘以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'递归调用?
答案 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
n
次sumK . 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