Haskell:递归地处理嵌套任意深度的列表

时间:2016-03-04 22:02:47

标签: list haskell recursion types

我正在学习Haskell,并希望编写函数来递归处理任意深度嵌套的列表。

例如,我想写recurReverse,在基本情况下,就像内置reverse一样,但是当传递嵌套列表时,reverse递归地列出了子列表的所有元素:

recurReverse [1,2]
>> [2,1]
recurReverse [[1],[2],[3,4]]
>> [[4,3],[2],[1]]
recurReverse [[[1,2]]]
>> [[[2,1]]]

目前我已经基本reverse向下:

rev [] = []
rev (h:t) = rev t ++ [h]

但我需要更多 - 在头部h也是一个列表(与LISP意义上的原子相对)的情况下,我希望能够{{ 1}} reverse并返回h之类的内容。当我尝试这样做时,我收到编译错误,说我不能rev t ++ [rev h],因为rev h属于rev类型,但我试图在类型上调用它[t] -> [t],这是有道理的。我怎么能绕过这个?

1 个答案:

答案 0 :(得分:7)

  

与LISP意义上的原子相反

嗯,Haskell中没有这样的东西。任何你不知道先验的类型(你不能,如果你正在对类型进行递归)可能是一个列表本身。没有原子性的概念,“not-list-being”你可以用作这种递归的基本案例。

也就是说,除非你明确区分。这可以在Haskell中很好地完成,使用GADT:

data Nest t where
   Egg :: t -> Nest t
   Nest :: [Nest t] -> Nest [t]

nestReverse :: Nest t -> Nest t
nestReverse (Egg x) = Egg x
nestReverse (Nest xs) = Nest . reverse $ map nestReverse xs

如果你不喜欢这个......好吧,还有另外一种方法,但它被认为是丑陋的/非Haskell-ish。

class Atomeous l where
  recurReverse :: l -> l

instance {-# OVERLAPPABLE #-} Atomeous l where
  recurReverse = id
instance Atomeous l => Atomeous [l] where
  recurReverse = reverse . map recurReverse

现在,recurReverse有您的预期行为。第一个例子是“原子”类型;因为它被标记为OVERLAPPABLE,所以如果编译器找不到“更好的”,它将只使用这个实例 - 这对于列表来说是正确的;这些会对所有元素进行递归调用。