在 Haskell 中使用递归和累加器

Using Recursion and Accumulators in Haskell

我正在尝试保护递归,但我在使用 Haskell 语法时遇到困难。
我正在尝试在 Haskell 中使用递归来减少整数 'n' 次直到它达到我想要的基本情况,同时跟踪 'n',然后 return 减少的整数和 'n' 作为我的信息的元组.

我的基本情况是整数在 1 到 13 之间。

例如,

Prelude> reduce 42
(3, 2)

Prelude> reduce 3
(3, 0)

为了解释逻辑,

Prelude> reduce 42
( (((42 - 13) - 13) - 13), 1+1+1+1 ) --> assume the accumulator = 0, hence 0,1,2.
(3, 3)

Prelude> reduce 3
(3, 0) --> already in base case!

目前,我有

reduce (x, 0)
  | x `elem` [1 .. 13] = (x, acc)
  | otherwise = reduce (x -13, acc + 1)
  where
    acc = 0

当然,我的 IDE 大喊我错了。我不知道如何将初始化元组实现到这个递归中,所以它就像 reduce :: a -> (a, b)
编辑:我想我越来越接近了……但当然,我正在尝试将其实现为 a -> (a,b)

此位置不允许使用下划线。

reduce::Int->Int                                                                                                                                                                        
 reduce x
    | elem x [1..13] = x
    | otherwise = reduce (x - 13)
 

您需要使用辅助函数对其进行初始化:

reduce::Int->(Int,Int)
reduce x = reduce' (x,0)

reduce'::(Int,Int)->(Int,Int)
reduce' (x,n)

  | elem x [1..13] = (x,n) 

  | otherwise = reduce' (x-13,n+1)

使用累加器参数是一种很好的方法,而且确实是最有效的方法,前提是您使用 seq 正确地管理惰性。然而,一种可以说更简单、也许更容易学习的技术将不使用累加器参数。相反,执行递归调用然后修改其结果:

reduce x | x `elem` [1..13] = (x, 0)
         | otherwise = let (y, n) = reduce (x - 13)
                       in (y, n + 1)

事实证明,“修改元组的第二个元素”有一个可爱的快捷方式 - 我不建议在您的第一个程序中使用它,但它可能是一个有趣的尝试:

reduce x | x `elem` [1..13] = (x, 0)
         | otherwise = succ <$> reduce (x - 13)