二维拉链

deo*_*ian 23 haskell functional-programming zipper multidimensional-array data-structures

最近关于Haskell中关于2d网格的问题的启发,我想知道是否有可能创建一个二维拉链来跟踪列表列表中的位置.列表中的一维拉链允许我们在大型列表中实际高效地移动本地(常见示例是文本编辑器).但是我们假设我们有这样的第二个维度:

grid = 
    [[ 1, 2, 3, 4, 5]
    ,[ 6, 7, 8, 9,10]
    ,[11,12,13,14,15]
    ,[16,17,18,19,20]
    ,[21,22,23,24,25]]
Run Code Online (Sandbox Code Playgroud)

我们是否可以创建某种拉链数据结构,不仅可以高效地左右移动,而且可以在网格中上下移动?如果是这样,如果我们用无限列表的无限列表替换列表列表,我们仍然可以获得有效的运动吗?

C. *_*ann 21

完全没有.拉链工作方式的一个关键方面是它们通过用于到达它的路径表示结构中的位置,以及沿途创建的额外片段,最终结果是您可以沿着该路径回溯并将结构重建为你走.因此,通过数据结构可用的路径的性质限制了拉链.

由于位置由路径标识,因此每个不同的路径表示不同的位置,因此具有多个相同值路径的任何数据结构都不能与拉链一起使用 - 例如,考虑循环列表或具有循环的任何其他结构路径.

2D空间中的任意移动并不真正符合上述要求,因此我们可以推断2D拉链必然会受到一定限制.也许你从原点开始,沿着这条路径走一条路,然后沿着那条路径回溯一段距离,以便到达其他点,例如.这也意味着对于结构中的任何点,还有其他点只能通过原点到达.

可以做的是建立2D距离的一些概念到数据结构,让您遵循路径向下穿过结构"之下"你靠近对方点; 我们的想法是尽量减少平均需要在2D空间移动一小段距离的回溯量.这最终与通过距离搜索2D空间所需的方法大致相同- 最近邻搜索,有效几何交叉,这种事情 - 并且可以使用相同类型的数据结构完成,即空间分区以创建更高 - 维搜索树.实现四叉树,kd树或类似结构的拉链很简单,就像任何其他树一样.

  • @AnupamJain:因为用于重建的片段是原始的,不可变结构的片段,如果其中一个片段包含到"相同"位置的另一个路径,则当您重新组合它时,该路径仍将具有原始值.处理它的唯一方法是沿着两条路径行走并进行两种替换 - 即将两条路径一起考虑为"唯一"路径. (8认同)
  • "拉链如何工作的一个关键方面是它们通过用于到达它的路径来表示结构中的位置".为什么有一条独特的路径是拉链的关键要求?我原以为在数据结构中表示"位置"的任何方式都足够了 (3认同)

Anu*_*ain 7

那么你可以使用像下面的代码一样简单的东西.我们通过所选元素的顶行,所选元素的底行,加上所选元素左侧的元素以及所选元素右侧的元素来表示一个表.

顶行和左元素以相反的顺序存储,以实现有效的移动.

我不确定这是否有资格作为拉链,因为即使我们在数据结构中持有"位置",它也不是"路径".

-- Table sel left right top bottom
data Table a = Table a [a] [a] [[a]] [[a]] deriving Show

left :: Table a -> Table a
left tab@(Table _ [] _ _ _) = tab
left (Table sel (l:ls) rs ts bs) = Table l ls (sel:rs) ts bs

right :: Table a -> Table a
right tab@(Table _ _ [] _ _) = tab
right (Table sel ls (r:rs) ts bs) = Table r (sel:ls) rs ts bs

up :: Table a -> Table a
up tab@(Table _ _ _ [] _) = tab
up (Table sel ls rs (t:ts) bs) = Table sel' ls' rs' ts (b:bs)
  where
    (ls',(sel':rs')) = splitAt (length ls) t
    b = ls ++ (sel:rs)

down :: Table a -> Table a
down tab@(Table _ _ _ _ []) = tab
down (Table sel ls rs ts (b:bs)) = Table sel' ls' rs' (t:ts) bs
  where
    (ls',(sel':rs')) = splitAt (length ls) b
    t = ls ++ (sel:rs)

tableToList :: Table a -> [[a]]
tableToList (Table sel ls rs ts bs) = (reverse ts) ++ [ls ++ (sel:rs)] ++ bs

listToTable :: [[a]] -> Table a
listToTable [] = error "cannot make empty table"
listToTable ([]:_) = error "cannot make empty table"
listToTable ((t:tr):ts) = Table t [] tr [] ts
Run Code Online (Sandbox Code Playgroud)

这甚至适用于无限列表 -

selected :: Table a -> a
selected (Table sel _ _ _ _) = sel

a :: Table Int
a = listToTable $ replicate 10 [1..]

selected a                   #=> 1
selected $ down a            #=> 1
selected $ right $ down a    #=> 2
Run Code Online (Sandbox Code Playgroud)

  • 这提供了与Zipper相同的操作,但它不是一个.由Huet引入的拉链每个导航步骤具有恒定的分配量.您的实现具有分配成本,这取决于总数据结构的大小.因此,对于您的用例,这可能是一个有用的数据结构,我不知道.但我不会称之为拉链. (4认同)
  • @jmg:公平地说,这*是一个拉链 - 具体来说,嵌套的两个标准列表拉链,在嵌套列表上运行.实际导航步骤沿着内部列表移动,或者当选择是内部列表的第一个元素时沿着外部列表移动.问题是"向上"和"向下"不是此拉链导航的一部分. (2认同)