这个关于Haskell的主题讨论了很多(例如mutable-array-implementation),但我仍然不确定需要频繁修改和随机访问数组/向量的情况的最佳实践是什么。
说一个长度为1,000,000的向量。对其进行操作涉及基于输入访问它的(小的,例如1000个)子集,并基于输入修改值。此外,这种操作重复2,000,000次。任务本身可以在纯数据结构中实现,例如列表如下,但效率非常低:
type Vect = [Int]
f :: Vect -> [[Int]] -> Vect
f x indsList = foldl g x indsList
-- g is just an example of random-access and modifications on the values.
g :: Vect -> [Int] -> Vect
g x inds = map h $ zip x [0..]
where h (x, i) = if i `elem` inds then x !! i + 1 else x !! i
散列/映射数据结构(例如IntMap)可以用于有效的大量随机访问,但是数组/向量也应该这样做。更重要的是,仍需要通过可变结构来解决大量修改以避免存储器复制。 Haskell中是否存在可变的随机访问数组/向量?如果使用ST / IO Monads,这些控件会影响我的设置吗?
答案 0 :(得分:6)
Haskell确实拥有高效的可变数组。
有STUArray
,它有相当复杂但通常只是不必要的Ix
索引方法,有许多边界检查和很少的特殊优化,这使得它比理论上可能慢一点
所有Data.Vector
的开销都很小,大量使用流融合优化,更喜欢简单的“列表式”界面。这意味着您可以将您的示例直接转换为不可变向量,并且仍然可以获得比预期更好的性能:
import Data.Vector.Unboxed as VU
type Vect = VU.Vector Int
f :: Vect -> [[Int]] -> Vect
f x indsList = VU.foldl g x indsList
g :: Vect -> [Int] -> Vect
g x inds = VU.zipWith h x [0..]
-- h is just an example of modifications on the values.
where h x i
| i`elem`inds = x VU.! i + 1
| otherwise = x VU.! i
是的,您可能希望在ST
monad中进行可变更新。不确定你的意思是“这些控制是否会影响性能”:一旦编译器优化了经过验证的类型安全性,就没有任何“控制”也不会出现在命令式语言中。哪个GHC可以做得很好;你可以使用Data.Vector.Unboxed
非常接近C性能。总有一些不可避免的开销,但这主要与垃圾收集等问题有关,你也可以用Java来解决这些问题。
由于ST
和IO
是monad,编译器实际上可以进行一些更高级的优化,而这些优化在命令式语言中是不可能的,尽管编译器还不是那么远
许多地方都讨论了性能,尤其是数组操作的性能,例如in RWH。
答案 1 :(得分:1)
Foreign UArrays是可变的,随机访问和最大快速。他们也“快而又脏”,我。即不要对每次突变操作施加冷冻/解冻样板。
缺点:几乎所有“低级别”操作都在IO
下。