Scala 递归列表连接

Scala 递归列表连接,scala,functional-programming,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 { cas

我有一个函数,它返回整数列表中元素之间的距离列表:

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
注释,尽管对
递归的调用似乎处于尾部位置

是否有
@tailrec
兼容的方式进行连接

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

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

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

距离函数只是一个例子来说明这个问题。

我不确定您是否可以使用累加器变量来确保正确的尾部调用优化。我用一个累加器模拟重做,最后是一个反转。您可以通过执行append而不是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(1,5,8,14,19,21).sliding(2).filterNot(_.isEmpty).map(list => list.last - list.head)

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

这不是对原始请求的回复,而是问题的替代解决方案

您只需将同一个列表“移位”一个位置,然后将生成的压缩列表映射到元组元素的差异即可

编码

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)

如果要对列表的相邻元素进行操作,
滑动
是您的朋友:

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

抱歉,这有点晚了,Haskell和ML中存在的标准模式在scala中也可以使用。这与cmbaxter的答案非常相似,但我想我应该添加它以供参考,这样的东西在我开始Scala时会对我有很大帮助

在列表中递归时始终使用累加器模式。此外,我在这里使用了curried形式来定义函数,而不是tuple形式

同样与您的代码相关,我将所有模式匹配语句放在递归函数本身内部,而不是外部val

  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)

我已经发现很多次,积累和逆转比我尝试的其他选择更快。这取决于具体情况,但不要害怕逆转。我认为积累+逆转是目前的做法。谢谢!除了累加和反转之外,通常的替代方法是差异列表的概念,即累加一个函数,该函数将通过追加而不是列表本身来构建列表。不过,这在JVM上可能不太管用……谢谢你的回答,但我对递归方法更感兴趣。我的具体用例有点复杂,一次调用
recurse
就可以在结果列表中添加几个项:
=>item1::item2::recurse(…)
距离函数只是一个示例来演示这个问题。我建议您将此注释显式添加到原始帖子中,为了避免人们错误地投票给这个答案,还请记住,若您可以使用高级操作编写显式递归,则通常不鼓励显式递归(因为它们在其他结构上工作,并且可能比您所做的更优化)。在您的例子中,如果每次迭代需要发出多个元素,您可以很容易地切换到
flatMap
,而不是
map
。我认为scala作为fp语言将提供一种比累积+反转更优雅的方法来解决这样一个基本问题,但我会接受它。谢谢我没有仔细考虑这里的细节,但是累积和反转是否是“正确”的解决方案取决于手头的算法,而不是所使用的语言。嗯,至少它不取决于所使用的特定语言,而是取决于它的评估策略。例如,在Haskell中,您可能会以非常不同的方式处理此问题,因为大多数人认为的尾部递归形式在这里不太常见。