在Haskell的递归期间保持变量不变

时间:2017-07-19 05:18:43

标签: haskell recursion

我试图从列表的每个实例中减去列表的平均值。但每次递归的平均值都会发生变化。如何使平均值保持静止?这就是我的尝试:

ave' :: (Fractional a, Real b) => [b] -> a
ave' xs = realToFrac (sum' xs) / (fromIntegral $ length xs)

std_series :: (Floating a, Real a) => [a] -> [a]
std_series [] = []
std_series (x:xs) = x - ave' (x:xs) : std_series xs

2 个答案:

答案 0 :(得分:5)

是否要从原始列表中的每个值中减去原始列表的平均值?如果是这样,您只需要map (subtract $ average xs) xs

答案 1 :(得分:2)

在实际代码中,我会使用map (subtract (ave' xs)) xs作为@SwiftsNamesake回答,或[x - m | let m = ave' xs, x <- xs]使用列表推导。

但是,如果你想手动编写它以更好地理解它,那么方法是计算一次平均值,然后调用一个辅助函数来执行递归。例如:

std_series xs = go xs
  where
    m = ave' xs
    go [] = []
    go (y:ys) = y - m : go ys

这也与称为 worker-wrapper transform 的有用技术有关,你有一个外部“包装器”函数,它设置一个包含一些固定参数的闭包,以及一个内部“工人”处理递归的函数。例如,由于这基本上是map但是手动内联,您可以查看为map的实现执行相同的转换:

map f [] = []
map f (x:xs) = f x : map f xs

在这里,我们将f传递给map的每个递归调用,但是我们不需要这样做因为它已经修复,所以我们可以添加一个仅在列表上运行的辅助函数,保持f关闭:

map f = go
  where
    go [] = []
    go (x:xs) = f x : go xs

如果您想为这些本地功能编写类型签名,有时您需要启用ScopedTypeVariables扩展名:

std_series :: forall a. (Fractional a) => [a] -> [a]
std_series xs = go xs
  where

    m :: a
    m = ave' xs

    go :: [a] -> [a]
    go [] = []
    go (y:ys) = y - m : go ys

此处,forall a.定义a的范围,以便您可以在mgo的签名中使用它。如果省略它,Haskell会将内部类型签名中的a视为不同的类型变量而不是外部a(恰好具有相同的名称),因此它会给你一个错误,如:

Couldn't match type ‘a’ with ‘a1’
  ‘a’ is a rigid type variable bound by
      the type signature for std_series ∷ Fractional a ⇒ [a] → [a]
  ‘a1’ is a rigid type variable bound by
       the type signature for m ∷ a1