【发布时间】:2012-01-31 22:55:35
【问题描述】:
我有一个Data.Vector 的Dog 记录,每个记录都标识了一个House 所说的狗住在哪里。我需要一个查找例程来查找住在房子里的所有狗,大致如下所示,但我需要持续时间查找,这是第一个版本无法提供的。
dogs_by_houses dogs h = [ d | d <- Vec.toList dogs, h == house d ]
据我了解,优化 Haskell 代码的核心规则是编译器只计算每个表达式在其封闭的 lambda 表达式中的一次。因此,在绑定h 之前,我必须在dogs_by_houses dogs 表达式中为这个特定的dogs 建立一个查找表,是吗?
我认为Data.Vector 是完成这项任务的最佳工具,尽管显然你不能像 C++ 向量那样缩小它们。我大致按如下方式实现:
dogs_by_houses :: Vec.Vector Dog -> Int -> [Dog]
dogs_by_houses dogs = let {
dog_house = house_id . house ;
v0 = Vec.replicate (maximum . map dog_house $ Vec.toList dogs) [] ;
f v d = let { h = dog_house d } in v // [(h,d:v!h)] ;
dbh = Vec.foldl' f v0 dogs
} in (dbh !)
这里有什么非常愚蠢的优化吗?我认为像 dbh 这样的变量上的严格标签不会有太大帮助,因为根据定义 dogs 必须在 dbh 有意义之前遍历。
使用MVector 和create 来代替折叠返回修改后的不可变向量有什么大的优势吗?到目前为止,我使用MVector 和create 的所有尝试都必须不那么简洁,dos 或fold (>>) 的各个层类似于构造或其他。我认为编译器应该简单地构建dbh,即使没有明确给出MVector。
这个算法不可能用列表实现吗?你偶尔会看到人们构建惰性无限的素数列表,然后用primes !! n 选择第 n 个素数。我假设以这种方式检索第 n 个素数需要在每次这样做时遍历列表中的前 n 个素数。相反,我注意到 GHC 将字符串存储为 C 字符串,而不是列表。编译器会简单地将已知列表元素表示为一个数组,而不是为每个元素重新遍历列表吗?
更新:
我已经使用 Paul Johnson 和 Louis Wasserman 的答案来构建一个函数来以这种方式索引任意向量,因为我必须基于几个不同的索引函数来这样做。
vector_indexer idx vec = \i -> (Vec.!) t i
where m = maximum $ map idx $ Vec.toList vec
t = Vec.accumulate (flip (:)) (Vec.replicate m [])
$ Vec.map (\v -> (idx v, v)) vec
dogs_by_houses = vector_indexer (house_id . house)
我还没有对此进行分析,但最终。我希望必须写 my_d_by_h = dogs_by_houses my_dogs 并调用 my_d_by_h 才能从索引中受益。
【问题讨论】:
-
GHC 仅对编译时已知的字符串执行此操作。
标签: algorithm haskell vector indexing