我有一个函数可以将二叉树缩减为给定顺序的节点值列表(中缀,前缀,后缀和反向中缀,前缀,后缀)。下面的代码可行,但我想知道是否有办法在不重复函数实现的情况下执行6次,只需使用不同的参数顺序:
data DOrd = Infix | Praefix | Postfix | GInfix | GPraefix | GPostfix
data BTree = Nil | BNode Int BTree BTree deriving (Eq,Ord,Show)
flatten :: BTree -> DOrd -> [Int]
flatten Nil _ = []
flatten (BNode n b1 b2) Infix = flatten b1 Infix ++ [n] ++ flatten b2 Infix
flatten (BNode n b1 b2) Praefix = [n] ++ flatten b1 Praefix ++ flatten b2 Praefix
---6 times basically the same for all the elements in DOrd
我考虑过功能或将DOrd从基本的枚举扩展到更复杂的结构 - 但我无法弄清楚如何。
答案 0 :(得分:6)
我不知道G版本的用途。但这可以解决我所理解的三个方面的共性:
data DOrd = Infix | Praefix | Postfix | GInfix | GPraefix | GPostfix
data BTree = Nil | BNode Int BTree BTree deriving (Eq,Ord,Show)
flatten :: BTree -> DOrd -> [Int]
flatten Nil _ = []
flatten (BNode n b1 b2) ord = case ord of
Praefix -> c ++ l ++ r
Infix -> l ++ c ++ r
Postfix -> l ++ r ++ c
...
where
l = flatten b1 ord -- left
r = flatten b2 ord -- right
c = [n] -- current
这只是将所有情况下相同的代码部分分解为短名称,这些名称不会掩盖根据DOrd值而变化的部分的逻辑。
答案 1 :(得分:5)
我认为为数据结构定义折叠函数在这里可以用来区分对结构进行递归的关注,并且它可能在代码的其他地方重用:
fold :: (Int -> a -> a -> a) -> a -> BTree -> a
fold _ x Nil = x
fold f x (BNode n b1 b2) = f n (fold f x b1) (fold f x b2)
现在,您可以轻松地用flatten
和fold
(受dave4420答案启发)写reorder
:
flatten :: DOrd -> BTree -> [Int]
flatten order = fold f []
where f n b1 b2 = mconcat $ reorder order [n] b1 b2
reorder Praefix a b c = [a, b, c]
reorder Infix a b c = [b, a, c]
...
请注意,我冒昧地将参数的顺序更改为flatten
。将数据结构作为最后一个参数使得它更容易与部分应用程序一起使用,并且它也使定义更整洁。
答案 2 :(得分:4)
将重新排序分成它自己的功能。
flatten :: BTree -> DOrd -> [Int]
flatten Nil _ = []
flatten (BNode n b1 b2) order = reorder order (flatten b1 order) [n] (flatten b2 order)
reorder :: Monoid m => DOrd -> m -> m -> m -> m
reorder Infix a b c = mconcat [a, b, c]
reorder Praefix a b c = mconcat [b, a, c]
reorder Postfix a b c = mconcat [a, c, b]
reorder GInfix a b c = mconcat [c, b, a]
reorder GPraefix a b c = mconcat [c, a, b]
reorder GPostfix a b c = mconcat [b, c, a]
您的原始代码仅需要它才能用于列表,但这适用于所有幺半群。