斯卡拉。惯用的扁平功能实现

时间:2014-06-07 08:44:26

标签: scala recursion functional-programming

我正在尝试在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))
}

此实现有效,但使用连接(我认为这很慢)。有人可以提供(或解释)没有列表连接的解决方案吗?

我用谷歌搜索了一下,但大多数关于内置展平的问题

1 个答案:

答案 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]