简体   繁体   English

有没有办法减少范围跟踪的痛苦?

[英]Is there some way to reduce the pain of range tracking?

There's currently a pull request by Jonathan S. to replace the implementation of Data.IntMap with one explained in this README based on ideas from a blog post by Edward Kmett. 公司目前旗下有一个拉请求由Jonathan S.,以取代实施Data.IntMap在解释一个本自述基于从想法博客文章由爱德华Kmett。

The basic concept Jonathan S. developed from is that an IntMap is a binary tree that looks like this (I've made some slight changes to his development for the sake of consistency): Jonathan S.开发的基本概念是IntMap是一个看起来像这样的二叉树(为了保持一致性,我对他的开发做了一些细微的改动):

data IntMap0 a = Empty | NonEmpty (IntMapNE0 a) 
data IntMapNE0 a =
    Tip !Int a
  | Bin { lo :: !Int
        , hi :: !Int
        , left :: !(IntMapNE0 a)
        , right :: !(IntMapNE0 a) }

In this representation, each node has a field indicating the least and greatest key contained in the IntMapNE0 . 在该表示中,每个节点具有指示IntMapNE0包含的最小和最大密钥的IntMapNE0 Using just a little bit fiddling allows this to be used as a PATRICIA trie. 使用一点点摆弄可以将其用作PATRICIA trie。 Jonathan noted that this structure has almost twice as much range information as it needs. Jonathan指出,这种结构的范围信息几乎是它所需要的两倍。 Following a left or right spine will produce all the same lo or hi bounds. 左侧或右侧脊柱将产生所有相同的lohi界限。 So he cut those out by only including the bound not determined by the ancestors: 因此,他只是包括未经祖先决定的界限而削减了这些:

data IntMap1 a = Empty | NonEmpty { topLo :: !Int, child :: !(IntMapNE1 a) }
data IntMapNE1 a =
    Tip a
  | IntMapNE1 { bound :: !Int
              , left :: !(IntMapNE1 a)
              , right :: !(IntMapNE1 a)

Now each node has either a left bound or a right bound, but not both. 现在每个节点都有左边界或右边界,但不是两个。 A right child will have only a left bound, while a left child will have only a right bound. 一个正确的孩子只有一个左边界,而一个左边的孩子只有一个右边界。

Jonathan makes one further change, moving the values out of the leaves and into the internal nodes, which places them exactly where they are determined. Jonathan做了一个进一步的改变,将值从叶子移动到内部节点,将它们精确地放置在它们确定的位置。 He also uses phantom types to help track left and right. 他还使用幻像类型来帮助跟踪左右。 The final type (for now, anyway) is 最后的类型(现在,无论如何)是

data L
data R
newtype IntMap a = IntMap (IntMap_ L a) deriving (Eq)
data IntMap_ t a = NonEmpty !Int a !(Node t a) | Empty deriving (Eq)
data Node t a = Bin !Int a !(Node L a) !(Node R a) | Tip deriving (Eq, Show)

Certain aspects of this new implementation are quite attractive. 这种新实现的某些方面非常有吸引力。 Most importantly, many of the most-used operations are substantially faster. 最重要的是,许多最常用的操作都要快得多。 Less importantly, but very nicely, the bit fiddling involved is much easier to understand. 不太重要,但非常好,所涉及的小小提琴更容易理解。

However, there is one serious pain point: passing the missing range information down through the tree. 然而,有一个严重的痛点:将缺失的范围信息传递到树中。 This isn't so bad for lookups, insertions, etc., but gets pretty seriously hairy in the union and intersection code. 这对于查找,插入等来说并不是那么糟糕,但在联合和交集代码中却非常严重。 Is there some abstraction that would allow this to be done automatically? 是否有一些抽象可以自动完成?

A couple extremely vague thoughts: 一对非常含糊的想法:

  1. Could the phantom types be used with a custom class to tie treatment directly to handedness? 幻影类型可以与自定义类一起使用,直接将治疗与手性联系起来吗?

  2. The "missing piece" nature is somewhat reminiscent of some zippery situations. “缺失的部分”性质有点让人联想到一些拉链情况。 Might there be a way to use ideas from that realm? 可能有办法使用该领域的想法吗?


I've started thinking about using an intermediate type of some sort to provide a symmetrical "view" of the structure, but I'm a bit stuck. 我开始考虑使用某种类型的中间类型来提供结构的对称“视图”,但我有点卡住了。 I can fairly easily convert back and forth between the basic structure and the fancy one, but that conversion is recursive. 我可以很容易地在基本结构和花哨的结构之间来回转换,但转换是递归的。 I need a way to convert only partially, but I don't know nearly enough about fancily built types to get it done. 我需要一种只能部分转换的方法,但我不太了解有关完美构建的类型以完成它。

Is there some abstraction that would allow this to be done automatically? 是否有一些抽象可以自动完成?

You should be able to define a set of pattern synonyms that give you that. 您应该能够定义一组模式同义词,为您提供。 I'll start from the second-to-last variant of your code, ie: 我将从您的代码的倒数第二个变体开始,即:

data IntMap1 a = Empty | NonEmpty { topLo :: !Int, child :: !(IntMapNE1 a) }
data IntMapNE1 a =
    Tip a
  | IntMapNE1 { bound :: !Int
              , left :: !(IntMapNE1 a)
              , right :: !(IntMapNE1 a)

We tuple such a value with the bound from the parent in an Either (indicating whether it is a low or a high bound). 我们用Either的父节点绑定这样的值(指示它是低限还是高限)。

viewLoHi (Left lo, IntMapNE1 hi left right)
    = Just (lo, hi, (Left lo, left), (Right hi, right)
viewLoHi (Right hi, IntMapNE1 lo left right)
    = Just (lo, hi, (Left lo, left), (Right hi, right)
viewLoHi _
    = Nothing

pattern Bin' lo hi left right <- (viewLoHi -> Just (lo, hi, left, right))

The top-level data type is different, so it needs its own pattern synonym 顶级数据类型不同,因此它需要自己的模式同义词

viewLoHi' (NonEmpty lo child) = viewLoHi (Left lo, child)
viewLoHi' Empty = Nothing

pattern NonEmpty' lo hi left right <- (viewLoHi' -> Just (lo, hi, left, right)

Using only NonEmpty' and Bin' as you traverse the tree, the bookkeeping should now be completely hidden. 在遍历树时只使用NonEmpty'Bin' ,现在应该完全隐藏簿记。 (Code not tested, so there will be typos here) (代码未经过测试,因此这里会有拼写错误)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM