递归列表连接

时间:2013-04-29 13:17:14

标签: scala functional-programming

我有以下函数返回整数列表元素之间的距离列表:

def dists(l: List[Int]) = {
  //@annotation.tailrec
  def recurse(from: Int, rest: List[Int]): List[Int] = rest match {
    case Nil => Nil
    case to :: tail => to - from :: recurse(to, tail)
  }

  l match {
    case first :: second :: _ => recurse(first, l.tail)
    case _ => Nil
  }
}

::阻止我使用@tailrec注释,尽管似乎对recurse的调用处于尾部位置。

是否有@tailrec兼容的方法来进行连接?

我可以使用累加器但是我必须反转输入或输出,对吗?

编辑:我对递归方法特别感兴趣。我的具体用例有点复杂,因为对recurse的一次调用可能会在结果列表中添加几个项目:

=> item1 :: item2:: recurse(...)

距离函数只是一个证明问题的例子。

4 个答案:

答案 0 :(得分:5)

这不是对确切原始请求的回复,而是解决问题的另一种方法。

您可以使用相同的列表“移位”一个位置来压缩列表,然后将生成的压缩列表映射到元组元素的差异。

代码

def dist(l: List[Int]) = l.zip(l drop 1) map { case (a,b) => b - a}

如果你无法理解发生了什么,我建议拆分操作并探索REPL

scala> val l = List(1,5,8,14,19,21)
l: List[Int] = List(1, 5, 8, 14, 19, 21)

scala> l zip (l drop 1)
res1: List[(Int, Int)] = List((1,5), (5,8), (8,14), (14,19), (19,21))

scala> res1 map { case (a, b) => b - a }
res2: List[Int] = List(4, 3, 6, 5, 2)

答案 1 :(得分:3)

我不确定您是否可以使用累加器变量执行您要执行的操作以确保正确的尾调用优化。我嘲笑了一个带有累加器的重做和最后的反向。你可以通过追加而不是prepend来消除反向,但我相信在创建更大的列表时,prepend / reverse组合会更有效。

object TailRec {
  def dists(l: List[Int]) = {
    @annotation.tailrec
    def recurse(from: Int, rest: List[Int], acc:List[Int]): List[Int] = rest match {
      case Nil => acc
      case to :: tail => 
        val head = to - from
        recurse(to, tail, head :: acc)
    }

    val result = l match {
      case first :: second :: _ => recurse(first, l.tail, List())
      case _ => Nil
    }
    result.reverse
  }

  def main(args: Array[String]) {
    println(dists(List(1,5,8,14,19,21)))

  }
}

现在,你想,你可以使用dists上提供的开箱即用功能来执行此List功能,如下所示:

List(1,5,8,14,19,21).sliding(2).filterNot(_.isEmpty).map(list => list.last - list.head)

这可能会降低效率,但更简洁。

答案 2 :(得分:1)

如果您想对列表中的邻居元素进行操作,sliding是您的朋友:

def dist(list: List[Int]) = list.sliding(2).collect{case a::b::Nil => b-a}.toList

答案 3 :(得分:1)

对不起有点迟到,Haskell和ML中存在的标准模式也适用于scala。这与cmbaxter的答案非常相似,但我认为我只是为了参考而添加它,当我开始使用Scala时,这样的东西会大大帮助我。

在递归列表时始终使用累加器模式。我也使用了在这里定义函数的curried形式而不是tupled形式。

还与您的代码有关,我将所有模式匹配语句放在递归函数本身中,而不是具有外部值。

  def build (l1: List[Int]) (acc: List[Int]): List[Int] =
l1 match {
    case Nil => acc
    case h::t => build (t) (acc.::(h))
    case _ => acc
}                                                 //> build: (l1: List[Int])(acc: List[Int])List[Int]    
  val list1 = List(0, 1, 2)                       //> list1  : List[Int] = List(0, 1, 2)
  val list2 = List(3, 4, 5, 6)                    //> list2  : List[Int] = List(3, 4, 5, 6)
  val b = build(list1)(list2)                   //> b  : List[Int] = List(6, 5, 4, 3, 0, 1, 2)