我需要一个二阶函数pairApply
,它将二元函数f
应用于列表式结构的所有唯一对,然后以某种方式将它们组合在一起。示例/草图:
pairApply (+) f [a, b, c] = f a b + f a c + f b c
一些研究让我相信Data.Vector.Unboxed
可能会有很好的表现(我还需要快速访问特定元素);对于Statistics.Sample
来说也是必要的,这将更加便利。
考虑到这一点,我有以下几点几乎编译:
import qualified Data.Vector.Unboxed as U
pairElement :: (U.Unbox a, U.Unbox b)
=> (U.Vector a)
-> (a -> a -> b)
-> Int
-> a
-> (U.Vector b)
pairElement v f idx el =
U.map (f el) $ U.drop (idx + 1) v
pairUp :: (U.Unbox a, U.Unbox b)
=> (a -> a -> b)
-> (U.Vector a)
-> (U.Vector (U.Vector b))
pairUp f v = U.imap (pairElement v f) v
pairApply :: (U.Unbox a, U.Unbox b)
=> (b -> b -> b)
-> b
-> (a -> a -> b)
-> (U.Vector a)
-> b
pairApply combine neutral f v =
folder $ U.map folder (pairUp f v) where
folder = U.foldl combine neutral
这不编译的原因是没有U.Vector (U.Vector a))
的Unboxed实例。我已经能够使用Data.Vector.Unboxed.Deriving
在其他情况下创建新的未装箱的实例,但我不确定在这种情况下它会如此简单(将其转换为元组对,其中第一个元素是所有内部的连接的向量,第二个是向量的长度,知道如何解包?)
我的问题可分为两部分:
请注意,我知道foldl
可能不是最佳选择;一旦我完成了实施,我计划用几个不同的折叠进行基准测试。
答案 0 :(得分:5)
无法为Unbox (U.Vector b)
定义经典实例,因为这需要预先分配一个内存区域,其中每个元素(即每个子向量!)具有相同的固定空间量。但总的来说,它们中的每一个都可能是任意大的,所以根本不可行。
原则上可能可以通过仅存储嵌套向量的扁平形式以及额外的索引数组(每个子向量开始的位置)来定义该实例。 I once briefly gave this a try;就可变向量而言,它实际上似乎有点有希望,但G.Vector
实例也需要一个可变实现,这对于这种方法是没有希望的(因为任何改变一个子向量中元素数量的突变都需要转移它背后的一切)。
通常,它不值得,因为如果单个元素向量不是很小,那么装箱它们的开销就无关紧要了,即使用B.Vector (U.Vector b)
通常是有意义的。
对于您的应用程序,我根本不会这样做 - 没有必要将上层元素选项包装在一个三角形数组中。 (并且这样做会非常糟糕,因为它会使算法采用 O ( n ²)内存而不是 O ( n )这就是所需要的。)
我会做以下事情:
pairApply combine neutral f v
= U.ifoldl' (\acc i p -> U.foldl' (\acc' q -> combine acc' $ f p q)
acc
(U.drop (i+1) v) )
neutral v
这很明显与明显的嵌套循环命令式实现相对应
pairApply(combine, b, f, v):
for(i in 0..length(v)-1):
for(j in i+1..length(v)-1):
b = combine(b, f(v[i], v[j]);
return b;
答案 1 :(得分:3)
我的回答与左下角的嵌套循环命令式实现基本相同:
pairApply :: (Int -> Int -> Int) -> Vector Int -> Int
pairApply f v = foldl' (+) 0 [f (v ! i) (v ! j) | i <- [0..(n-1)], j <- [(i+1)..(n-1)]]
where n = length v
据我所知,我认为此实现没有任何性能问题。
为了简单起见,非多态。