如何实现`append [A](x:List [A],y:List [A]):在尾递归版中列出[A]`?

时间:2015-07-23 16:07:49

标签: scala recursion tail-recursion

以下是两个列表的append的递归版本:

def append[A](x: List[A], y: List[A]): List[A] = x match {
  case Nil => y
  case h :: t => h :: append(t, y)
}

如何将其转换为尾递归版?

2 个答案:

答案 0 :(得分:3)

试试这个,尽管x以相反的顺序排在前面。这是打算吗?

import scala.annotation.tailrec

@tailrec
def append[A](x: List[A], y: List[A]): List[A] = x match {
  case Nil => y
  case h :: t => append(t, h :: y)
}

如果你想按照它的顺序加上x,你必须做这样的事情:

import scala.annotation.tailrec

def append[A](x: List[A], y: List[A]): List[A] = {
  @tailrec
  def innerAppend[A](x: List[A], y: List[A]): List[A] = x match {
    case Nil => y
    case h :: t => innerAppend(t, h :: y)
  }

  innerAppend(x.reverse, y)
}

答案 1 :(得分:2)

Scala标准库的序列foldLeft方法是一种尾递归方法,允许创建许多核心序列操作,这些操作在其元素之间应用二进制操作。

使用foldLeft,以下是您实施追加方法的方法:

def append[A](x: List[A], y: List[A]): List[A] =
  x.reverse.foldLeft(y)((t, h) => h :: t) //Note the swapped order for h and t

将返回附加列表,其顺序与包含reverse

的顺序相同
append(List(1,2,3), List(4,5,6))
res0: List[Int] = List(1, 2, 3, 4, 5, 6)

缺少@tailrec注释并不意味着这不是尾递归;相反,@tailrec在通过尾随递归方法(如foldLeft)实现方法或函数时,将无法正确检测到。

foldLeft不同,标准库中的foldRight创建尾递归方法,尽管其语法比必须反转输入列表更容易理解交换参数。这就是为什么我更喜欢实现foldRightViaFoldLeft方法,因为它具有更加用户友好的语法,并保留尾递归属性:

def foldRVL[A,B](l: List[A], z: B)(f: (A,B) => B): B = //"RVL" == "Right Via Left"
  l.reverse.foldLeft(z)((b, a) => f(a, b)) //Note again that we are swapping a and b

def append[A](x: List[A], y: List[A]): List[A] =
  foldRVL(x, y)(_ :: _) //the swap in foldRVL means that we don't have swap when using it

这仍然是尾递归,但现在更容易阅读。 foldRVL和其他fold方法有很多种用途。尽可能使用它们。有关折叠的更多信息以及有关其递归属性的更好解释,我建议您阅读以下文章:Scala Code Review: foldLeft and foldRight

我也强烈推荐这本书, Scala中的函数编程,从中我学会了如何正确地fold