在Haskell中构建(惰性)地图的有效方法

时间:2018-08-25 10:46:12

标签: haskell

我知道在Haskell中构建Map的几种不同方法:

  1. 使用fromList从列表中构建
  2. 使用fromAscList从列表中构建列表
  3. 使用MapMonoid(或Semigroup)并合并singletons的事实。

我知道#1的摊销复杂度为O(n * log(n)),其中#2为O(n)。 我猜想#3应该大致相当于#1,并且可能会融合。

摊销很重要,因为默认情况下Haskell是惰性的,即使从Map中查找为O(log(n)),实际上也可以与Map的构造交错本身为O(n * log(n)),实际上可以使查找为O(n * log(n))(尤其是如果您每次需要时都在构建地图时)。如果您使用硬编码的地图,也可能会附加

例如,我是否认为lookup 'b' (fromList [('a', 1), ('b', 2)])实际上等效于在列表中查找d而无需使用中间Map?

#1和#3之间有什么区别,或者对列表和调用fromList进行排序吗?

更新

此外,如果我只需要计算一次映射,是否需要确保GHC不内联,以便在函数之间共享?

用例

我意识到这个问题可能有点模糊,实际上与我最近遇到的不同用例相对应。

第一个对应于“静态连接”。我有一个管理商品的应用,每个商品代码都可以按样式和变化形式进行拆分(例如'T-Shirt-Red'=>('T-Shirt','Red')。拆分基于规则(和regexp),而且速度很慢。为了避免每次都重新计算规则,只需执行一次并存储在db表中。我有一些纯函数需要拆分项目代码,因此我将它们传递给函数{ {1}}。该函数实际上是部分应用于地图的查找。代码与此类似

Text -> (Text, Text)

通过使用getSplitter :: Handler (Text -> (Text, Text)) getSplitter = do sku'style'vars <- runDB $ rawSQL "SELECT sku, style, var FROM style_cache " [] -- load the split table let sku'map = fromList [ (sku, (style,var)) | (sku, style, var) <- sku'style'vars ] return $ flip lookup sku'map 对项目进行排序并使用sku(实际上比fromDistinctAscList更快)可以很容易地加快速度。但是我仍然有一些关于如何在不同请求之间缓存它的问题。

第二种情况是在两个表之间进行手动联接,我通常会做一些事情

fromAscList

再次可以在SQL中对sku'infos进行排序,并使用fromDistinctAscList。

第三种情况是在与不同表中的类别项目有关的其他信息的获取中。

例如,我希望能够按类别比较销售(销售表)和购买(购买表)。

在纯SQL中,我会做些事情

do
   sku'infos <- selectList [] [] -- load item info
   let skuInfo = fromList sku'infos

   orderLines <- selectList [] [] -- load orders
   -- find infos corresponding to order items
   mapM (\o -> (o, lookup (orderSku o) skuInfo) orderLines

但是,这是一个简化的示例,实际上,聚合要复杂得多,必须在Haskell和联接中完成。为此,我要分别加载每个表(对sql中的内容进行分组)并返回SELECT style, sum(sales.amount), sum(purchase.amount) FROM style_cache LEFT JOIN sales USING(sku) LEFT JOIN purchases USING(sku) GROUP by style Map Style SalesInfo等...并合并它们。该表很大,我意识到我最终将所有内容加载到内存中,而我可能可以通过手动“压缩”文件来提高效率,但是我不确定如何加载。

1 个答案:

答案 0 :(得分:2)

我不确定我是否理解这个问题的全部动机,但我会发表一些评论:

  1. Map是严格限制的-这意味着Map的树结构和键本身被强制(至少足够远,可以进行所有必要的比较) }操作。因此,我希望Map进行O(n * log(n))比较(n为Data.Map.lookup k (fromList xs)的长度),而我期望xs进行O(n)比较(实际上只是进行相等性检查,但通常它的复杂度与比较差不多。
  2. 如果Prelude.lookup k xs的速度确实比fromAscList . sort快,则这是fromList中的性能错误,应只更改库以定义Data.Map。如果真是这样,我会感到非常惊讶。人们花了很多时间优化fromList = fromAscList . sort,所以我不希望看到有那么低的成果。
  3. 是的,内联中断共享。