Haskell init和tails如何在Data.Sequence中工作?

Haskell init和tails如何在Data.Sequence中工作?,haskell,data-structures,sequence,Haskell,Data Structures,Sequence,Louis Wasserman在Data.Sequence中编写了inits和tails的当前实现。他指出,它们非常高效,事实上,只要看看代码,我就可以看出,无论它们在做什么,它们都是以一种干净的、自上而下的方式来做的,这种方式往往会为懒惰的树带来良好的性能。不幸的是,我不知道他们在做什么。谁能帮我一把吗?代码有点长,但可以在上找到。是我 我认为最好的方法可能是通过一个例子。让我们来试试 Deep (Two 1 2)

Louis Wasserman在
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
这些成为inits树的后缀数字
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 -}))