Haskell init和tails如何在Data.Sequence中工作?
Louis Wasserman在Haskell init和tails如何在Data.Sequence中工作?,haskell,data-structures,sequence,Haskell,Data Structures,Sequence,Louis Wasserman在Data.Sequence中编写了inits和tails的当前实现。他指出,它们非常高效,事实上,只要看看代码,我就可以看出,无论它们在做什么,它们都是以一种干净的、自上而下的方式来做的,这种方式往往会为懒惰的树带来良好的性能。不幸的是,我不知道他们在做什么。谁能帮我一把吗?代码有点长,但可以在上找到。是我 我认为最好的方法可能是通过一个例子。让我们来试试 Deep (Two 1 2)
Data.Sequence
中编写了inits
和tails
的当前实现。他指出,它们非常高效,事实上,只要看看代码,我就可以看出,无论它们在做什么,它们都是以一种干净的、自上而下的方式来做的,这种方式往往会为懒惰的树带来良好的性能。不幸的是,我不知道他们在做什么。谁能帮我一把吗?代码有点长,但可以在上找到。是我
我认为最好的方法可能是通过一个例子。让我们来试试
Deep (Two 1 2) (Two 7 8))
(Deep (One (Node2 3 4)) (One (Node2 5 6))
Empty
这是一个稍微简化的序列(例如省略元素包装)
让我们在这上面做一点;尾巴基本上是对称的。我们的递归算法将省略空init,只包含非空的内容
前缀 因此,inits的前缀数字基本上是用fmap digitToTree(initsDigit(Two 1 2))生成的 这是整个过程的前两个init,这个数字将是
inits
结果的前缀数字。(除非我们在完成所有操作后准备预结束空序列,但现在我们忽略它。)
内部树 现在让我们来看一下内部树的inits,它被视为
FingerTree(节点a)
——我们现在还不打算分离节点,它只是一个包含两个节点的两元素FingerTree
。我不打算做这个的细节,它只是通过相同的算法递归,我只是想神奇地得到结果
Deep
(One (Single (Node2 3 4)))
Empty
(One (Deep (One (Node2 3 4)) Empty (One (Node2 5 6))))
:: FingerTree (FingerTree (Node a))
这些是内部树的初始化。这些如何对应于外部树的init?内部树的每个init对应于包含
- 原始树的前缀数字,
Two 1 2
- 除了内部树的init的最后一个
节点
- 内部树init的最后一个
节点的某个前缀
FingerTree(节点a)
将映射到节点(FingerTree a)
,其中包含FingerTree(节点a)
中最后一个节点的每个初始化的FingerTree
因此,例如,Single(Node2 3 4)
及其最后提取的节点将分解为Empty
和Node2 3 4
,生成的节点(FingerTree a)
为
Node2
(Deep (Two 1 2 {- prefix of original tree -})
Empty
(One 3 {- first prefix of Node2 3 4 -}))
(Deep (Two 1 2)
Empty
(Two 3 4 {- second prefix of Node2 3 4 -}))
对于内部树的另一个前缀,Deep(One(Node2 3 4))Empty(One(Node2 5 6))
,提取最后一个节点
将得到剩余的Single(Node2 3 4)
和提取的节点Node2 5 6
,因此得到的节点(FingerTree a)
:
因此,这是一个将FingerTree(节点a)
(内部树的单个初始化)带到节点(FingerTree a)
的操作。因此,递归地获取了内部树的inits作为FingerTree(FingerTree(Node a))
,我们将此函数映射到它们上,以获得FingerTree(Node(FingerTree a))
,这正是我们想要的;它是整个事物初始的内部树
后缀 最后,还有原始树的init,由
- 原始前缀
- 原始内部树
- 原始树后缀的每个init
initsDigit(Two-7-8)
返回Two(One-7)(Two-7-8)
,我们基本上只是映射\sf->Deep pr m sf
,以获得
Two
(Deep (Two 1 2 {- original -})
(Deep (One (Node2 3 4)) Empty (One (Node2 5 6)) {- original -})
(One 7 {- first init of original suffix digit -}))
(Deep (Two 1 2 {- original -})
(Deep (One (Node2 3 4)) Empty (One (Node2 5 6)) {- original -})
(Two 7 8 {- second init of original suffix digit -}))
因此,这并不是代码的组织方式。我们已经描述了一个从
FingerTree a
到FingerTree(FingerTree a)
的函数,但实际的实现本质上是加上一个fmap
,因为我们总是需要以某种方式映射元素——即使它只是包装新类型。但这基本上就是我们正在做的。据我所知,它们是使用手指树实现的。我不是专家,但你可以在和@akegalj中找到更多信息,我熟悉序列的表示方式;瓦瑟曼的代码只是有点。。。神奇。或者,你可以把我叫到这里,等我有时间看一下我几年前写的代码时,我会回答的。我必须承认这仍然让我有点头晕,但我会继续努力解决它。谢谢你花时间解释。我想我现在明白了它的大意。这似乎只是勉强奏效,几乎是偶然的。手指树很奇怪。您是否有机会了解我对数据.Sequence
的
、*>
和zipWith
的重新实现?我终于把
和*>
做成了不错的形状,尽管早期版本把它做成了容器-0.5.6.3
。我已经好几年没有注意到这一点了,但我可以明天再看一看。
Node2
(Deep (Two 1 2 {- prefix of original tree -})
(Single (Node2 3 4) {- init of the inner tree minus the last Node -})
(One 5 {- first prefix of Node2 5 6 -})
(Deep (Two 1 2 {- prefix of original tree -})
(Single (Node2 3 4) {- init of the inner tree minus the last Node -})
(Two 5 6 {- second prefix of Node2 5 6 -}))
Two
(Deep (Two 1 2 {- original -})
(Deep (One (Node2 3 4)) Empty (One (Node2 5 6)) {- original -})
(One 7 {- first init of original suffix digit -}))
(Deep (Two 1 2 {- original -})
(Deep (One (Node2 3 4)) Empty (One (Node2 5 6)) {- original -})
(Two 7 8 {- second init of original suffix digit -}))