在Haskell中用':'替换'++'。错误

时间:2016-03-19 23:43:01

标签: haskell

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 = [],它会导致错误。那么如何才能使我的解决方案达到最优?

2 个答案:

答案 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