这是“使用延迟模态从定点算子计算(无限)树”的变体。
那个设定。我们研究一种二叉树语言,增强了通过从根开始的路径引用树中任意其他节点的能力:
type Path = [Bool]
data STree = SNode STree STree
| SPath Path
| SLeaf Int
deriving (Show)
路径是在某个根的上下文中定义的,当我们在路径中看到 False/True 时,它会识别通过连续跟随左/右子节点找到的子树。例如,路径在树中[False, True]
标识。SLeaf 2
SNode (SNode (SLeaf 1) (SLeaf 2)) (SLeaf 3)
例如,此类树可用于识别任意流图(包括不可约图,这是使用固定点运算符无法实现的。)
我们可以考虑由这种描述诱导的无限树。
data Tree = Node Tree Tree
| Leaf Int
deriving (Show)
这是一个从一个到另一个的转换函数,利用一个辅助函数follow
在树的某个路径上找到子树:
follow :: Path -> Tree -> Tree
follow [] s = s
follow (False : p) (Node s _) = follow p s
follow (True : p) (Node _ s) = follow p s
follow _ (Leaf _) = error "bad path"
flatten' :: Tree -> STree -> Tree
flatten' root (SPath p) = follow p root
flatten' root (SNode s1 s2) =
Node (flatten' root s1) (flatten' root s2)
flatten' _ (SLeaf i) = Leaf i
flatten :: STree -> Tree
flatten s = fix (\root -> flatten' root s)
不幸的是,这个函数没有生产力:它在flatten (SPath [])
.
问题。我们现在考虑这种结构的变体,它增加了延迟模态(如“使用延迟模态从定点算子计算(无限)树”中所述),以及Loop
指示自引用循环的构造函数。
data Tree = Node (D Tree) (D Tree)
| Leaf Int
| Loop
deriving (Show)
不使用非结构递归函数调用(因此,您可以在STree
and上进行结构递归Path
),编写一个STree -> Tree
展开无限树的函数(或类似函数)。
后记。在许多情况下,我们不想计算无限展开:如果存在,我们想要有限展开,否则会出错。具体来说,给定原始数据类型:
data Tree = Node Tree Tree
| Leaf Int
deriving (Show)
在不使用非结构递归的情况下,我们可能想要编写一个函数STree -> Maybe Tree
(或类似函数),如果它存在则将语法展开为有限树,否则失败。这种结构中没有延迟模态保证了它是有限的。
由于此结构中没有延迟模态的实例,因此似乎无法使用fixD
: 我们将得到一个我们永远无法使用的延迟结果。看来我们必须将树转换为图,对其进行拓扑排序,然后直接实现高斯消元算法,而不使用fixD
.
是否有替代的打字规则允许我们像在原始(错误)代码中一样优雅地实现展开?如果是这样,您可能刚刚(重新)发现了另一种在图中查找循环的算法。