使用翻转:运算符对二叉树进行后序遍历。左手边评估右手边?

时间:2016-03-03 06:09:03

标签: haskell binary-tree

给出二叉树

data BinaryTree a =
    Leaf
  | Node (BinaryTree a) a (BinaryTree a)
  deriving (Show)

我的预订/订购解决方案是使用:

完成的
preorder :: BinaryTree a -> [a]
preorder Leaf = []
preorder (Node left x right) = x : preorder left ++ preorder right

inorder :: BinaryTree a -> [a]
inorder Leaf = []
inorder (Node left x right) = inorder left ++ (x : inorder right)

但是对于后期订单,这没有用:

postorder :: BinaryTree a -> [a]
postorder Leaf = []
postorder (Node left x right) = (postorder left ++ postorder right) : x

并且理所当然,因为我发现类型签名是

(:) :: a -> [a] -> [a]

我以为我可以通过以下方式解决这个问题:

append :: [a] -> a -> [a]
append = flip (:)

postorder :: BinaryTree a -> [a]
postorder Leaf = []
postorder (Node left x right) = (postorder left ++ postorder right) `append` x

但它最终给了我与preorder相同的答案。我感到很困惑,因为我认为左手边会在右手边进行评估。

我认为这是因为我知道postorder也可以像这样解决(不是这种方法的粉丝,因为它意味着无缘无故地将x作为一个列表而是追加):

postorder (Node left x right) = postorder left ++ postorder right ++ [x]

有人可以告诉我哪里出错了,也许为什么邮购顺序与预订相同?

1 个答案:

答案 0 :(得分:2)

让我们使用等式推理来查看postorderappend失败的位置。

append = flip (:)

(postorder left ++ postorder right) `append` x
-- bring append to front
append (postorder left ++ postorder right)  x
-- substitute append
flip (:) (postorder left ++ postorder right) x
-- expand flip
(\f x y -> f y x) (:) (postorder left ++ postorder right) x
-- partially apply expanded flip
(\x y -> y : x) (postorder left ++ postorder right) x
-- finish application
x : (postorder left ++ postorder right)

现在,因为我们知道一般(x : y) ++ z === x : (y ++ z)我们可以看到你的" postorder"方法实际上等同于您的预订方法!

你所犯的错误在于试图考虑评价的顺序,以及像#34这样的想法的理由;左手边将在右手边评估"。从这种等式方法可以看出,在纯懒惰的功能设置中,评估顺序基本上可以忽略不计。重要的是通过替换和简化函数本身来实现。

如果您不想将x添加到列表中只是为了追加它,请执行此操作,编写函数

consEnd :: [a] -> a -> [a]

直接在列表末尾插入内容。