在Scala中展开任意嵌套集合的通用、类型安全的方法?

在Scala中展开任意嵌套集合的通用、类型安全的方法?,scala,scala-collections,Scala,Scala Collections,有时,我会花一些时间来使用Scala,尽管在我自己的工作中(到目前为止)无法使用Scala,但它的混合功能仍然吸引着我。对于kicks,我决定以最通用的方式尝试前几款——操作并返回任何类型的适用集合。最初的几个问题并不太难,但我发现自己完全被flatte所困扰。我就是搞不懂怎么打这种东西 具体地说,我的问题是:是否可以编写一个类型安全函数来展平任意嵌套的SeqLikes?那么说, flatten(List(Array(List(1, 2, 3), List(4, 5, 6)), Array(Li

有时,我会花一些时间来使用Scala,尽管在我自己的工作中(到目前为止)无法使用Scala,但它的混合功能仍然吸引着我。对于kicks,我决定以最通用的方式尝试前几款——操作并返回任何类型的适用集合。最初的几个问题并不太难,但我发现自己完全被
flatte
所困扰。我就是搞不懂怎么打这种东西

具体地说,我的问题是:是否可以编写一个类型安全函数来展平任意嵌套的
SeqLike
s?那么说,

flatten(List(Array(List(1, 2, 3), List(4, 5, 6)), Array(List(7, 8, 9), List(10, 11, 12))))
会回来吗

List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12): List[Int]
??请注意,这与Haskell和Scala习题集中的问题不同;我正在尝试编写一个函数,该函数不是将异构列表展平,而是将同质但嵌套的序列展平


在网上搜索时,我发现了一个关于这个问题的列表,但它会运行并返回一个列表[任何]。这需要某种类型的递归,对吗?或者我是在说这比实际情况更难吗?

您面临的问题与他们在Haskell中描述的问题相同:Scala中没有异构的
列表。幸运的是,您可以遵循Haskell解决方案中的完全相同的路径

定义一些可以嵌套的数据类型:

sealed trait NestedList[A]
case class Elem[A](a: A) extends NestedList[A]
case class AList[A](a: List[NestedList[A]]) extends NestedList[A]
然后为该类型编写一个通用展平函数:

def flatten[A](l: NestedList[A]): List[A] = l match {
  case Elem(x) => List(x)
  case AList(x :: xs) => flatten(x) ::: flatten(AList(xs))
  case AList(Nil) => Nil
}
甚至

def flatten[A](l: NestedList[A]): List[A] = l match {
  case Elem(x) => List(x)
  case AList(x) => x.flatMap(flatten)
}
用法:

flatten(AList(Elem(1) :: Elem(2) :: AList(Elem(3) :: Nil) :: Nil))

当然,我们也可以将其作为一种方法直接添加到
嵌套列表
特征中。

似乎正确的做法就是调用
。将
的次数展平:

scala> val x = List(Array(List(1, 2, 3), List(4, 5, 6)), Array(List(7, 8, 9), List(10, 11, 12)))
x: List[Array[List[Int]]] = List(Array(List(1, 2, 3), List(4, 5, 6)), Array(List(7, 8, 9), List(10, 11, 12)))

scala> x.flatten.flatten
res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

因为Scala是类型化的,所以您总是提前知道特定变量的嵌套深度。由于您提前知道了这一点,因此处理任意结构没有多大价值,就好像您不确定需要调用多少次
。flatten

以下内容适用于Scala 2.10.0-M7。您需要为
Array
支持添加额外的案例,并可能对其进行优化,使其具有更具体的输出集合类型,但我想可以从这里开始:

sealed trait InnerMost {
  implicit def innerSeq[A]: CanFlatten[Seq[A]] { type Elem = A } =
    new CanFlatten[Seq[A]] {
      type Elem = A
      def flatten(seq: Seq[A]): Seq[A] = seq
    }
}
object CanFlatten extends InnerMost {
  implicit def nestedSeq[A](implicit inner: CanFlatten[A]) 
  : CanFlatten[Seq[A]] { type Elem = inner.Elem } =
    new CanFlatten[Seq[A]] {
      type Elem = inner.Elem
      def flatten(seq: Seq[A]): Seq[inner.Elem] =
        seq.flatMap(a => inner.flatten(a))
    }
}
sealed trait CanFlatten[-A] {
  type Elem
  def flatten(seq: A): Seq[Elem]
}

implicit final class FlattenOp[A](val seq: A)(implicit val can: CanFlatten[A]) {
  def flattenAll: Seq[can.Elem] = can.flatten(seq)
}

// test        
assert(List(1, 2, 3).flattenAll == Seq(1, 2, 3))
assert(List(Seq(List(1, 2, 3), List(4, 5, 6)), Seq(List(7, 8, 9),
                List(10, 11, 12))).flattenAll == (1 to 12).toSeq)

问我的问题时,我可能没有完全清楚或准确,因为这不是我的想法。我并不是要编写一个在异构列表上运行的扁平函数,而是一个在同构但嵌套的seq上运行的扁平函数,如我的示例所示。我很好奇(1)它是否可能(2)如果可能的话如何写。不过谢谢你。啊,好的。然后haskell的例子有点误导了我。不适用于混合Int和string的列表请将你的
反向
问题作为一个单独的问题,因为它与主要问题无关。请看这里:差不多了,除了我不能很好地把它伪装成序列类型的泛型。这是一个公平的回答,尽管我仍然好奇这样一个函数是否可以编写。:)非常好,谢谢!我将把这个标记为已回答,尽管我可能需要一些时间来完成它。老实说,如果我一开始就知道有那么多代码,我就不会在问之前那么努力了对于
CanFlatten
,仅使用两个类型参数可能更容易。它涉及到类型成员,因为否则隐式不能完全自行解析。我认为,如果您不需要
nestedSeq.flattAll
,但
flattAll(nestedSeq)
就足够了(即不需要“拉皮条”),它会变得更容易,更像链接的问题(“这是如何递归的…”)。