data Tree a = Node a (Tree a) (Tree a) | Empty
toList :: (Tree a) -> [a]
toList (Node v l r ) = (toList l) ++ [v] ++ (toList r)
toList Empty = []
正如我们所知,它不是最佳的,因为每个++
都与O(n)操作相连,以便连接列表。替代解决方案是使用:
而不是++
。但由于事实toList Empty = []
,它会导致错误。那么如何才能使我的解决方案达到最优?
答案 0 :(得分:7)
您无法直接执行此操作,因为:
仅将单个元素添加到列表中。但是在这两个子分支中,你通常会给出多个元素。缓慢的递归实现需要来解决这个问题!
所以,要走的是使用具有更高效串联操作的容器!这些在图书馆中可用,例如sequence。但是有一种容器类型可以让你很快自己酿造出来:
newtype DList a = DList { getDList :: [a] -> [a] }
instance Monoid (DList a) where
mempty = DList id
mappend (DList l1) (DList l2) = DList $ l1 . l2
singletonD :: a -> DList a
singletonD x = DList (x:)
有了这个,你可以做到
toDList :: Tree a -> DList a
toDList (Node v l r) = toDList l <> singletonD v <> toDList r
toDList Empty = mempty
这是您定义的精确翻译,但它不会像连接普通列表时遇到相同的性能问题。
因为这些difference lists非常容易实现,所以在Haskell中很常见,只是在没有进一步提及的情况下进行内联:
toList :: (Tree a) -> [a]
toList t = tdl t []
where tdl (Node v l r) = toList l . (v:) . tdl r
tdl Empty = id
答案 1 :(得分:1)
您需要以不同方式将事物放在一起以实现目标。您不能将++
替换为:
。试试这个:
toList t = toListPlus t []
toListPlus :: Tree a -> [a] -> [a]
toListPlus t xs
应该生成toList t ++ xs
,但是使用toListPlus
的递归调用来实现,而不是使用++
或toList
。让我们来解决它。基本案例很简单:
toListPlus Empty xs = xs
递归案例也不算太糟糕。我们想要将左子树转换为列表,在其后粘贴其他内容:
toListPlus (Node v l r) xs =
toListPlus l ???
之后会发生什么?根,然后转换正确的子树的结果,然后是任何被添加的内容:
toListPlus (Node v l r) xs =
toListPlus l (v : toListPlus r xs)
此函数使用隐式堆栈来跟踪剩余的工作。这可能是最有效的方法。如果您愿意,可以使用拉链样式表示来显示堆栈。
此解决方案与左侧描述的解决方案有何关系?嗯,他们实际上是一样的。我们可以通过将列表参数转移到:
来看到toListPlus Empty = \xs -> xs
toListPlus (Node v l r)
= \xs -> toListPlus l (v : toListPlus r xs)
= toListPlus l . (v :) . toListPlus r