flatten'
是尾递归的,因此不应该占用任何堆栈空间。但它会沿着树的左侧向下走,在堆中产生一堆thunks。如果您在示例树上调用它并将其减少到WHNF,则应该得到类似于以下内容的东西:
:
/ \
1 flatten' Tip :
/ \
2 flatten' (Node 4) []
/ \
(Node 3) Tip
/ \
Tip Tip
该算法的时间复杂度为
O(N)
,但它必须检查
Tip
和
Node
。
这似乎是按顺序展平树的最有效方法。
Data.Tree
模块有一个
flatten
函数
在这里,它做了类似的事情,只是它更喜欢先序遍历。
更新:
在图形缩减引擎中,
main
中的
flatten
将生成以下图形:
@
/ \
@ []
/ \
/ \
/ \
flatten' Node 2
/ \
/ \
/ \
Node 1 Node 4
/ \ / \
Tip Tip / \
/ \
Node 3 Tip
/ \
Tip Tip
为了将其减少至WHNF,图形缩减引擎将展开脊柱,将
[]
和
Node 2
推入堆栈。然后它将调用
flatten'
函数,该函数将重写图形为:
@
/ \
/ \
/ \
@ :
/ \ / \
/ \ 2 \
/ \ \
flatten' Node 1 \
/ \ \
Tip Tip @
/ \
@ []
/ \
/ \
/ \
flatten' Node 4
/ \
/ \
/ \
Node 3 Tip
/ \
Tip Tip
将从栈中弹出两个参数。根节点仍未处于 WHNF 状态,因此图形缩减引擎将展开脊柱,将 2:...
和 Node 1
推入栈中。然后它将调用 flatten'
函数,该函数将重写图形为:
@
/ \
/ \
/ \
@ :
/ \ / \
/ \ 1 \
/ \ \
flatten' Tip @
/ \
/ \
/ :
@ / \
/ \ 2 \
/ Tip @
/ / \
flatten' @ []
/ \
/ \
/ \
flatten' Node 4
/ \
/ \
/ \
Node 3 Tip
/ \
Tip Tip
将从堆栈中弹出两个参数。根节点仍未进入WHNF,因此图形缩减引擎将展开脊柱,将1:...
和Tip
推入堆栈。然后它将调用flatten'
函数,该函数将重写图形为:
:
/ \
1 \
\
@
/ \
/ \
/ :
@ / \
/ \ 2 \
/ Tip @
/ / \
flatten' @ []
/ \
/ \
/ \
flatten' Node 4
/ \
/ \
/ \
Node 3 Tip
/ \
Tip Tip
并将从堆栈中弹出两个参数。我们现在处于 WHNF 状态,最多使用了两个堆栈条目(假设 Tree
节点不是需要额外堆栈空间来评估的惰性求值)。
因此,flatten'
是尾递归的。它可以替换自身,而无需评估其他嵌套的 redexes。第二个 flatten'
仍然是一个位于堆上的 thunk,而不是位于堆栈上。