二维拉链
受到最近关于 Haskell 中二维网格的问题的启发,我想知道是否可以创建一个二维拉链来跟踪列表列表中的位置。列表上的一维拉链使我们能够真正有效地在大型列表中进行本地移动(常见的示例是文本编辑器)。但是假设我们有这样的第二个维度:
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]]
我们能否创建某种拉链数据结构,以便不仅可以在网格中左右移动,还可以上下移动?如果是这样,如果我们用无限列表的无限列表替换列表的列表,我们仍然可以获得高效的移动吗?
Inspired by the recent question about 2d grids in Haskell, I'm wondering if it would be possible to create a two-dimensional zipper to keep track of a position in a list of lists. A one-dimensional zipper on a list allows us to really efficiently move locally in a large list (the common example being a text editor). But lets say we have a second dimension like this:
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]]
Can we create some kind of zipper data structure to efficiently move not only left and right but up and down in the grid here? If so, what if we replace the list of lists with an infinite list of infinite lists, can we still get efficient movement?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不完全是,不。拉链工作原理的关键方面之一是它们通过用于到达结构的路径来表示结构中的位置,加上沿途创建的额外片段,最终结果是您可以沿着该路径回溯并重建结构,如下所示你去。因此,通过数据结构可用的路径的性质限制了拉链。
因为位置是由路径标识的,所以每个不同的路径代表不同的位置,因此具有相同值的多个路径的任何数据结构都不能与拉链一起使用 - 例如,考虑循环列表或任何其他具有循环的结构路径。
2D 空间中的任意运动并不真正符合上述要求,因此我们可以推断 2D 拉链必然会受到一定的限制。例如,您可能会从原点开始,沿着结构行走一条路径,然后沿着该路径回溯一段距离以到达其他点。这也意味着对于结构中的任何点,都存在只能通过原点到达的其他点。
您可以做的就是在数据结构中构建一些二维距离的概念,这样当您沿着结构向下的路径时,您“下方”的点彼此接近;这个想法是为了最大限度地减少在 2D 空间中移动短距离所需的平均回溯量。这最终与按距离搜索 2D 空间所需的方法大致相同——最近邻搜索、高效的几何交集等——并且可以使用相同类型的数据结构来完成,即空间分区来创建更高维的搜索树。为四叉树实现一个拉链,一个kd-tree 或类似的结构很简单,就像任何其他树一样。
Not quite, no. One of the key aspects of how zippers work is that they represent a location in a structure by a path used to reach it, plus extra fragments created along the way, with the end result that you can backtrack along that path and rebuild the structure as you go. The nature of the paths available through the data structure thus constrains the zipper.
Because locations are identified by paths, each distinct path represents a different location, so any data structure with multiple paths to the same value can't be used with a zipper--for example, consider a cyclic list, or any other structure with looping paths.
Arbitrary movement in 2D space doesn't really fit the above requirements, so we can deduce that a 2D zipper would necessarily be somewhat limited. Perhaps you'd start from the origin, walk a path through the structure, and then backtrack along that path some distance in order to reach other points, for example. This also implies that for any point in the structure, there are other points that can only be reached via the origin.
What you can do is build some notion of 2D distance into the data structure, so that as you follow a path down through the structure, the points "below" you are close to each other; the idea is to minimize the amount of backtracking needed on average to move a short distance in 2D space. This ends up being roughly the same approach needed to search 2D space by distance--nearest neighbor searches, efficient geometric intersection, that sort of thing--and can be done with the same kind of data structure, namely space partitioning to create a higher-dimensional search tree. Implementing a zipper for a quadtree, a kd-tree, or similar structures is straightforward, just like any other tree.
那么您可以使用像下面的代码这样简单的东西。我们通过所选元素的顶行、所选元素的底行、以及所选元素左侧的元素和所选元素右侧的元素来表示表格。
顶行和左侧元素以相反的顺序存储,以实现高效移动。
我不确定这是否符合拉链的条件,因为即使我们在数据结构中保留一个“位置”,它也不是一个“路径”。
这甚至适用于无限列表 -
Well you can use something simple like the following code. We represent a table by the top rows of the selected element, the bottom rows of the selected element, plus the elements to the left of the selected one, and the elements to the right of the selected one.
The top rows and the left elements are stored in a reverse order to enable efficient movement.
I'm not sure if this qualifies as a zipper though, because even though we hold a "location" in the data structure, it is not a "path".
This even works for infinite lists -
我一直在寻找类似的东西:一种廉价且轻松地导航(包括“向后”)双无限列表列表的方法。这是我的看法。
如果我仔细阅读其他答案,我在这里展示的实际上并不是一个拉链:虽然导航的分摊时间为 O(1),但拉链
结构使用的内存网络从未被释放。另一方面,它应该足以让“单元”能够共享,无论我们采取什么路径到达它们,这就是我们想要的二维列表列表的拓扑类型。作为补偿,用于生成它的列表列表最终应该不被引用并被垃圾收集。
数据结构应该是不言自明的。向上和向左可以严格,因为我们是从单链表构建的。 AFAIK,在 Haskell 中让他们变得懒惰是没有意义的,因为他们不会让任何东西超出范围。
网格是递归构建的,用
Nothing
扩展所提供输入的边界。我需要的zipWith
的足够懒的变体的灵感来自我关于该主题的另一系列问题的答案.实际操作如下:
通过删除“Maybe”,界面可能会变得更加易于使用。当然,风险由您自己承担。
这可能有点偏离主题,因为它也不是真正的拉链,但它解决了我的问题;由于这是我第一次寻找解决方案时出现的问题,因此我将其发布在这里,旨在对其他人有所帮助。
I was looking for something similar: a way to cheaply and easily navigate (which includes going “backwards”) a doubly-infinite list of lists. Here's my take at it.
If I read the others answers carefully, what I'm presenting here isn't really a zipper: while navigation is amortized O(1), the memory used by the zipper
structurenetwork is never released. On the other hand, it ought to tie the knot enough for “cells” to be shared no matter the path we take to get to them, which is the kind of topology we'd want on a 2D list of lists.To compensate, the list of lists used to generate it ought to eventually go unreferenced and garbage-collected.
The data structure ought to be self-explanatory. Up and left can afford to be strict because we're building from singly-linked lists. AFAIK, there's no point in maing them lazy in Haskell, as they wouldn't let anything go out of scope anyway.
The lattice is built recursively, expanding the borders of the provided input with
Nothing
. The lazy-enough variants ofzipWith
I needed are inspired from answers to another series of questions of mine on the topic.Here it is in action:
The interface can likely be made even easier to use by dropping the
Maybe
s. At your own risk, naturally.This might be slightly off-topic as it's not a real zipper either, but it solved my problem; and since this is the question that came up when I first looked for a solution, I'm posting it here with the intent it helps someone else.