Louis Wasserman在inits
中编写了tails
和Data.Sequence
的当前实现。他表示他们非常有效率,而且只是看着我能看到的代码,无论他们做什么,他们都是以干净,自上而下的方式做到这一点,这往往会导致懒树的良好表现。不幸的是,我实际上无法正视他们正在做的事情。任何人都可以帮我一把吗?代码有点长,但可以在Hackage上找到。
答案 0 :(得分:7)
是我!
我认为最好的方法可能是通过一个例子。让我们一起去......
Deep (Two 1 2) (Two 7 8))
(Deep (One (Node2 3 4)) (One (Node2 5 6))
Empty
这是一个有点简化的序列(例如省略了Elem包装器)。
让我们来做这件事;尾巴基本上是对称的。我们的递归算法将省略空的init并且只包括非空的东西。
<强>前缀强>
因此,inits的前缀数字将基本上由fmap digitToTree (initsDigit (Two 1 2))
生成。
initsDigit (Two 1 2) = Two (One 1) (Two 1 2)
fmap digitToTree (Two (One 1) (Two 1 2)) =
Two (Single 1) (Deep (One 1) Empty (One 2))
所以这些是整个事物的前两个部分,这个数字将成为inits
结果的前缀数字。 (除非我们在完成所有操作后才会在前面添加空序列,但让我们暂时忽略它。)
内树
现在让我们来看看内树的内容,将其视为FingerTree (Node 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对应一个包含
的树Two 1 2
Node
之外的所有内容Node
的最后一个前缀因此,通过获取内树的init获取的每个FingerTree (Node a)
将映射到Node (FingerTree a)
,其中包含FingerTree
,用于FingerTree (Node a)
中最后一个节点的每个init 。
例如,提取最后一个节点的Single (Node2 3 4)
将被分解为Empty
和Node2 3 4
,结果Node (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))
,提取最后一个Node
会为我们提供余数Single (Node2 3 4)
和提取的节点Node2 5 6
,因此得到的结果为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 -}))
。节点(FingerTree a)
FingerTree (Node a)
因此,这是一个将Node (FingerTree a)
(内树的单个init)转换为FingerTree (FingerTree (Node a))
的操作。因此,以递归方式获取内树的内容为FingerTree (Node (FingerTree a))
,我们将此函数映射到它们上以获得initsDigit (Two 7 8)
,这正是我们想要的;它是整个事物内在的内在树。
<强>后缀强>
最后,还有由
组成的原始树的内容这些成为了inits树的后缀数字。 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
的函数,但实际的实现基本上是加{{1}},因为我们最终总是需要以某种方式映射元素 - 甚至如果它只是包装新类型。但这基本上就是我们正在做的事情。