当在Haskell中通过Tridiagonal matrix algorithm求解线性方程组时,我遇到了以下问题。
我们有三个向量:a
,b
和c
,我们想要制作第三个向量c'
,它们是它们的组合:
c'[i] = c[i] / b[i], i = 0 c'[i] = c[i] / (b[i] - a[i] * c'[i-1]), 0 < i < n - 1 c'[i] = undefined, i = n - 1
在Haskell中对上述公式的简单实现如下:
calcC' a b c = Data.Vector.generate n f where n = Data.Vector.length a f i = | i == 0 = c!0 / b!0 | i == n - 1 = 0 | otherwise = c!i / (b!i - a!i * f (i - 1))
由于重复,看起来这个函数calcC'
具有复杂性 O ( n 2 )。但我们实际需要的是将一个以前生成的值传递给内部函数f
。
我编写了自己的generate
版本,复杂度为 O ( n )和帮助函数mapP
:
mapP f xs = mapP' xs Nothing where mapP' [] _ = [] mapP' (x:xs) xp = xn : mapP' xs (Just xn) where xn = f x xp generateP n f = Data.Vector.fromList $ mapP f [0 .. n-1]
如您所见,mapP
的作用类似于标准map
,但也会传递给先前生成的值的映射函数或第一次调用的Nothing
。
我的问题:在Haskell中有什么漂亮的标准方法吗?我不是要重新发明一点吗?
感谢。
答案 0 :(得分:10)
有两个名为mapAccumL
和mapAccumR
的标准功能可以完全按照您的需要进行操作。
mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
mapAccumR :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
基本上,它们的行为类似于fold
和map
。
map f = snd . mapAccumL (\_ x -> (() , f x) ()
foldl f b = fst . mapAccumL (\b x -> (f b x, () ) b
答案 1 :(得分:1)
如果你使用懒惰的Data.Array,你可以通过在定义c'时引用c'直接表示重现。
答案 2 :(得分:1)
在我的情况下,以下代码似乎是上面公式的最简单实现:
import qualified Data.Vector.Generic as V calcC' a b c = V.postscanl' f 0.0 $ V.zip3 a b c where f c' (a, b, c) = c / (b - a * c')
感谢Vector
的作者添加了有用的postscanl'
方法。