【问题标题】:Haskell: recursively process lists nested arbitrarily deepHaskell:递归处理任意深度嵌套的列表
【发布时间】:2016-06-18 19:55:10
【问题描述】:

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

例如,我想写recurReverse,在基本情况下,它的作用就像内置的reverse,但是当传递一个嵌套列表时,reverse 子列表的所有元素递归为好吧:

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

目前我有基本的reverse down:

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

但我需要的不止这些——如果头部 h 也是一个列表(而不是 LISP 意义上的原子),我希望能够 reverse h以及返回类似rev t ++ [rev h] 的内容。当我尝试这样做时,我得到一个编译器错误,说我不能rev h,因为rev[t] -> [t] 类型,但我试图在t 类型上调用它,这是有道理的。我该如何解决这个问题?

【问题讨论】:

  • 归根结底,您需要为列表编写一个类型,该类型在类型级别对嵌套深度进行编码,以便您可以编写不在 list 上的递归函数 但在 嵌套深度 上。可能最简单的方法是使用类型族。

标签: list haskell recursion types


【解决方案1】:

相对于 LISP 意义上的原子

嗯,Haskell 中没有这样的东西。任何你不知道先验的类型(如果你对类型进行递归,你就不能知道)可能是一个列表本身。没有原子性和“非列表存在”的概念,您可以将其用作此递归的基本情况。

也就是说,除非您明确区分。这可以在 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 风格。

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,所以编译器只有在找不到“更好的”时才会使用这个实例——这正是列表的作用;这些对所有元素进行递归调用。

【讨论】:

  • 谢谢!如果我想绕过 Haskell 的类型系统并使用“动态”递归数据结构(例如树),那么可以肯定地说几乎总是需要多态性吗?
  • 一点也不!多态性对于编写通用代码非常有用,但是您完全可以将递归数据结构单独编写为单态 ADT。请注意,Haskell 中的树与嵌套列表完全不同。列表特别具有 flat / homogeneous 结构,与 Lisp 中的结构非常不同。
猜你喜欢
  • 2017-01-13
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-10
相关资源
最近更新 更多