main = print . findAllLeaves $ F N (F (F N N) (F N (F N N)))
data Tree = N | F Tree Tree deriving Show
data Dir = L | R deriving Show
type Path = [Dir]
descend :: Tree -> Dir -> Tree
descend (F l _) L = l
descend (F _ r) R = r
descend _ _ = undefined
findAllLeaves :: Tree -> [Path]
findAllLeaves N = [[]]
findAllLeaves tree = do dir <- [L, R]
map (dir:) $ findAllLeaves (descend tree dir)
结果
[[L],[R,L,L],[R,L,R],[R,R,L],[R,R,R,L],[R,R,R,R]]
我使用列表单子挑L和R “同时”,和下降两个分支。得爱不确定性!
说明:
我希望descend
是非常明显的。你给它一棵树和一个方向,它沿着这个方向下降。
findAllLeaves
是有趣的一个。它是如何工作的?
我们将在一分钟内讨论基本情况,findAllLeaves N = [[]]
。
递归大小写在列表monad中,使用do
表示法。第一行很简单:选择L
或R
并将其分配给dir
。 这个list monad实际上会选择两个,并且将每个结果都连接起来并将它们连接在一起。这是他们理解的关键。这正是你所要求的:什么是以L
开头的所有路径,以及从R
开始的所有路径是什么?把它们放在一起,你有从当前节点到其后代叶节点的所有路径。
第二行应该从上一段的描述中相当清楚。在给定方向下降树(descend tree dir
),从该点找到所有树叶(findAllLeaves
),然后将选择的方向预先分配给每个子路径(map (dir:)
)。
那么为什么基本情况下[[]]
?那么,请考虑一下基本案例之上的案例。所以,例如,findAllLeaves (F N N)
。当我们选择dir = L
,我们评估了下联:
map (L:) $ findAllLeaves (descend (F N N) L)
递减留下给我们只是N
map (L:) $ findAllLeaves N
然后,我们已经打的基本情况:
map (L:) $ [[]]
现在你能看看为什么我们有这个奇怪的基本情况?列表中有一个空的列表?因为我们要将(L:)
映射到它上面,换句话说,将L
预先添加到外部列表中的每个列表。结果如下:
[[L]]
当我们在dir = R
时得到了类似的结果。
[[R]]
那么接下来列表单子串接那些一起
[[L]] ++ [[R]]
而且我们最终
[[L], [R]]
如果任何这仍不清楚,请让我知道在评论和我会尽力澄清。
这是一棵二叉树,对吧?它是否分类?你现在有什么代码? – 2011-05-31 18:44:37
是的,它没有排序,我有这个代码到目前为止,已更新 – Lunar 2011-05-31 18:45:50
您可以缩进四个空格的代码片段,以使它们的格式如此。您也可以突出显示它们并按下标有'{}'的按钮。 – fuz 2011-05-31 19:07:04