在Scala中,如何组合同一List的元素序列?

时间:2014-03-18 10:44:55

标签: scala functional-programming

如何转换此列表:

List("a", "b", "c", "d")

进入包含以下元素的列表

List(List("a"), List("a", "b"), List("a", "b", "c"), List("a", "b", "c", "d"))

我的要求是从包含目录名称的列表构建相对目录路径列表,其中a是根目录,ba的叶子,即{{1 }}

示例:

a/b

转换为:

"fruit", "tropical", "mango"

编辑:我可以迭代地执行此操作,但我正在寻找功能性解决方案。

2 个答案:

答案 0 :(得分:7)

您可以使用inits来实现您正在寻找的类似内容:

val xs = List("a", "b", "c", "d")
val ys = xs.inits.toList.reverse.drop(1)

说明:

xs.inits.toList会给你这个结果:

List(List(a, b, c, d), List(a, b, c), List(a, b), List(a), List())

现在你可以反转它并删除第一个元素并得到它:

List(List(a), List(a, b), List(a, b, c), List(a, b, c, d))

然后,只需输入String个结果:

ys.map(_.mkString("/")) // results in List(a, a/b, a/b/c, a/b/c/d)

答案 1 :(得分:1)

我认为你应该使用inits(我会避免依赖返回元素的顺序,尽管记录了最后一个元素是空元素):

val basket = List("fruit", "tropical", "mango")
basket.inits.toList filterNot (_.isEmpty) sortBy (_.length) map (_ mkString "/")

但是,如果您想要一种不使用该库函数的方法,您可以使用自己的递归函数:

def paths(elems: List[String]): List[List[String]] = {
  elems match { 
    case Nil     => Nil
    case e :: es => List(e) :: (paths(es) map (e :: _))
  }
}
paths(basket) map (_ mkString "/")

这不是尾递归的,所以如果你的路径很多元素,它将会破坏堆栈。您可以使用累积参数使其尾递归(实际上,我可以做的最好的是两个累积参数):

@annotation.tailrec
final def paths(elems: List[String], path: List[String], acc: List[List[String]]): List[List[String]] = {
  elems match {
    case Nil     => acc
    case e :: es => paths(es, path :+ e, acc :+ (path :+ e))
  }
}
paths(basket, Nil, Nil) map (_ mkString "/")

此解决方案虽然在:+上使用了List运算符(追加元素),但它在时间复杂度方面并不是最优的。我将把它作为读者的练习(提示:您可能希望以相反的顺序存储累积参数)。