我正在尝试在Scala中实现flatten函数。 我完成了这样的事情:
// implementation
def flatten(xs: List[Any]): List[Any] =
xs match {
case List() => List()
case y::ys => y match {
case k::ks => flatten(List(k)) ::: flatten(ks) ::: flatten(ys)
case _ => y :: flatten(ys)
}
}
// something like tests
def main(args: Array[String]){
val f1 = flatten(List(List(1, 1), 2, List(3, List(5, 8))))
assert(f1 == List(1, 1, 2, 3, 5, 8))
val f2 = flatten(List(List(List(1), List(1)), 2, List(3, List(5, 8))))
assert(f2 == List(1, 1, 2, 3, 5, 8))
}
此实现有效,但使用连接(我认为这很慢)。有人可以提供(或解释)没有列表连接的解决方案吗?
我用谷歌搜索了一下,但大多数关于内置展平的问题
答案 0 :(得分:4)
对于初学者来说,正如@ om-nom-nom指出的那样,如果没有解决List[Any]
问题,谈论任何惯用语都没有意义。让我们看看我们是否可以更好地描述这一点。
sealed trait Tree[A]
case class Node[A](l: List[Tree[A]]) extends Tree[A]
case class Leaf[A](a: A) extends Tree[A]
def flatten[A](tree: Tree[A]): List[A]
现在填补空白变得容易一些。
def flatten[A](tree: Tree[A]): List[A] = {
def flattenRec(acc: List[A], t: Tree[A]): List[A] = t match {
case Leaf(a) => a :: acc
case Node(ll) => ll.foldLeft(acc)(flattenRec)
}
flattenRec(Nil, tree).reverse
}
但是,如果我们使用scalaz为我们的Tree
添加一些额外的功能,那么这将变得更加容易,事实上可以帮助您做任何您想要对列表的扁平列表做的事情。在这里,我提供了scalaz.Foldable[Tree]
的定义。
import scalaz._
import Scalaz._
object Tree {
implicit def treeFoldable = new Foldable[Tree] {
override def foldMap[A, B](fa: Tree[A])(f: (A) => B)(implicit F: Monoid[B]): B = {
fa match {
case Leaf(a) => f(a)
case Node(l) => l.foldLeft(F.zero)((acc, tree) => F.append(acc, foldMap(tree)(f)))
}
}
override def foldRight[A, B](fa: Tree[A], z: => B)(f: (A, => B) => B): B = fa match {
case Leaf(a) => f(a, z)
case Node(l) => l.foldRight(z)((tree, zz) => foldRight[A, B](tree, zz)(f))
}
}
}
现在我们的扁平变得简单
def flatten2[A](tree: Tree[A]): List[A] = {
Foldable[Tree].foldLeft(tree, List.empty[A])((acc, a) => a :: acc).reverse
}
或使用可折叠语法导入
def flatten2[A](tree: Tree[A]): List[A] = {
tree.foldLeft(List.empty[A])((acc, a) => a :: acc).reverse
}
如果我们有Tree[Int]
,我们可以将所有值相加
val numbers: Tree[Int] = Node(List(Leaf(1), Node(List(Leaf(2), Leaf(3))), Leaf(4)))
val sum = numbers.foldLeft(0)(_ + _)
事实证明,scalaz已经有一个非常相似的Tree,我发现它非常有用。区别在于scalaz.Tree包含A
,每个Node[A]
。