我试图从列表的每个实例中减去列表的平均值。但每次递归的平均值都会发生变化。如何使平均值保持静止?这就是我的尝试:
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
答案 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
的范围,以便您可以在m
和go
的签名中使用它。如果省略它,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