如何创建函数以从任意深度嵌套的列表中生成平面列表?

时间:2019-04-28 16:08:05

标签: scala shapeless

是否可以在scala中编写函数,该函数将采用任意深度嵌套的列表的列表并将其递归转换为平面列表?例如:

flatten(List(List(1), List(List(2), 3), 4))

应该返回

List(1,2,3,4)

我尝试了shapeless,但没有效果:

object flatten extends (List ~> List) {
    def apply[T](s: List[T]) = s.map {
       case l: List[T] => apply(s)
       case x => x
    }
}

这给了我

  

类型不匹配

     

找到:列表[任何]

     

必填:列表[T]

如果它可以演绎正确的类型(在示例List[Int]而不是List[Any]的情况下,那也很棒)

2 个答案:

答案 0 :(得分:8)

问题是,您不会在输入中收到List[T],但会收到List[Any],其中AnyTList[Any]的混合。

因此,如果您知道叶子元素的类型,则可以通过在TT上进行递归模式匹配来潜在地使用类型参数List[Any]来表示它和平面图元素:< / p>

import scala.reflect.ClassTag

def flatten[T: ClassTag](list: List[Any]): List[T] =
  list.flatMap {
    case x: T => List(x)
    case sub: List[Any] => flatten[T](sub)
  }

flatten[Int](List(List(1), List(List(2), 3), 4))
// List[Int] = List(1, 2, 3, 4)

答案 1 :(得分:2)

您所需的flatten本质上是未键入的。您正在将元素(假设它们的类型为E),元素列表(List[E]),元素列表(List[List[E]])等放在一个列表中,其类型必须为List[Any],因为其元素没有任何共同之处。 Shapeless就是关于具有描述性类型并在它们之间进行转换的,因此它对您没有任何帮助。此外,请查看函数的定义:

def apply[T](s: List[T]) = s.flatMap { // should be flatMap, conceptually
   case l: List[T] => apply(l) // should be l, conceptually
   case x => List(x) // should be singleton list, conceptually
}

因此,sList[T],而s.map依次给您T中的每一个。然后,您使用类型case,并在其中一个分支中检查l: T是否实际上是l: List[T]。也就是说,您检查List[T] <: T。这很奇怪,表示您的功能不正确。

如果您真的想使用Shapeless,请使用类型准确描述您的输入。我们希望此接口用于flatten[T]

  • 如果收到t: T,则返回List(t): List[T]
  • 如果它收到l: List[X],其中Xflatten[T]的有效输入,则它会展平每个X,然后将结果串联为一个大List[T]
  • 如果它收到h: H,其中H <: HList,其中H的每个元素是对flatten[T]的有效输入,则将每个元素展平,并将结果串联为一个List[T]

这是它的实现:

object flatten extends Poly1 {
  implicit def caseT[T] = at[T](List(_))
  implicit def caseList[T, X](implicit e: Case.Aux[X, List[T]])
    = at[List[X]](_.flatMap(e))
  implicit def caseHNil[T, N <: HNil] = at[N](_ => List[T]())
  implicit def caseHCons[T, H, R <: HList](implicit hf: Case.Aux[H, List[T]],
                                                    rf: Case.Aux[R, List[T]])
    = at[H :: R] { case h :: r => hf(h) ++ rf(r) }

  final class Specialized[T] {
    def apply[X](x: X)(implicit c: Case.Aux[X, List[T]]): List[T] = c(x)
  }
  def apply[T]: Specialized[T] = new Specialized
}

使用方式:

scala> flatten[Int]((1 :: HNil) :: ((2 :: HNil) :: 3 :: HNil) :: 4 :: HNil)
List(1, 2, 3, 4)
scala> flatten[Int](1 :: List(2, 3) :: List(List(4, 5), List(), List(6, 7)) :: List(8 :: List(9, 10) :: HNil, 11 :: List(12, 13, 14) :: HNil) :: HNil)
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)

替代方法是简单地使用正确的数据结构。在这种情况下,正确的结构称为List上的自由monad,也称为玫瑰树:

sealed trait Free[M[+_], +A] {
  def flatten(implicit M: Monad[M]): M[A]
}
case class Pure[M[+_], +A](x: A) extends Free[M, A] {
  override def flatten(implicit M: Monad[M]) = M.pure(x)
}
case class Step[M[+_], +A](step: M[Free[M, A]]) extends Free[M, A] {
  override def flatten(implicit M: Monad[M]) = step.flatMap(_.flatten)
}
// for convenience
object Rose {
  type Rose[+A] = Free[List, A]
  type Leaf[+A] = Pure[List, A]
  type Branch[+A] = Step[List, A]
  object Leaf {
    def apply[A](x: A): Leaf[A] = Pure(x)
    def unapply[A](x: Leaf[A]): Some[A] = Some(x.x)
  }
  object Branch {
    def apply[A](xs: Rose[A]*): Branch[A] = Step(xs.toList)
    def unapplySeq[A](xs: Branch[A]): Some[List[Rose[A]]] = Some(xs.step)
  }
}
// specialized:
// sealed trait Rose[+A] { def flatten: List[A] }
// case class Leaf[+A](x: A) extends Rose[A] { override def flatten = List(x) }
// case class Branch[+A](x: List[Rose[A]]) extends Rose[A] { override def flatten = x.flatMap(_.flatten) }

用法:

scala> Branch(Branch(Leaf(1)), Branch(Branch(Leaf(2)), Leaf(3)), Leaf(4)).flatten