如何迭代这个尾递归方法?

时间:2013-01-08 16:43:29

标签: scala recursion tail-recursion

在下面的Scala方法中,方法xs如何遍历列表nthxs.tail是递归调用的,但为什么尾部不总是相同的值,因为特性def tail中的List只返回参数化类型列表?

object nth {

  def nth[T](n: Int, xs: List[T]): T =
    if (xs.isEmpty) throw new IndexOutOfBoundsException
    else if (n == 0) xs.head
    else {
        nth(n - 1, xs.tail)
        }                                 //> nth: [T](n: Int, xs: week4.List[T])T
  val list = new Cons(1, new Cons(2, new Cons(3, new Nil)))
  nth(2 , list)   > res0: Int=3   
}

trait List[T] {
  def isEmpty: Boolean
  def head: T
  def tail: List[T]
}
class Cons[T](val head: T, val tail: List[T]) extends List[T]{
  def isEmpty = false
}
class Nil[T] extends List[T]{
  def isEmpty = true
  def head : Nothing = throw new NoSuchElementException("Nil.head")
  def tail : Nothing = throw new NoSuchElementException("Nil.tail")
}     

2 个答案:

答案 0 :(得分:7)

List是一个递归结构。请参阅Wikipedia article on Cons。这是来自那篇文章:

enter image description here

您要开始的结构是new Cons(42, new Cons(69, new Cons(613, new Nil)))。虽然tail方法也返回List[Int]的实例,即不是相同的列表,但是跟随其中一个右箭头的子列表。

因此,如果在您的示例中,您将从Cons(1, Cons(2, Cons(3, Nil)))开始,请n2

  • 在函数nth第一次迭代中,我们问:Cons(1, Cons(2, Cons(3, Nil)))是否为空?没有!是n == 0吗?不。所以用尾巴递归并n递减。
  • 在此第二次次迭代中,我们会问:Cons(2, Cons(3, Nil))是否为空(这又是List[Int])?不是n == 0?不(现在是1)。转到下一个递归。
  • 第三次次迭代中,我们会问:Cons(3, Nil)是否为空?不,是n == 0。是!因此,请返回Cons(3, Nil)的{​​{1}}的头部。

答案 1 :(得分:3)

您已递归地定义了List类型。这意味着,您正在使用其他列表来创建新列表。当然,你必须以某种方式制作第一个List,这就是你定义Nil的原因。

因此,您可以创建一个没有其他列表的空列表:

val empty = new Nil[Int]                  //> empty  : Nil[Int] = Nil@1f93f8

并且您可以使用已创建的列表创建非空列表,如果您有一个n-1大小的列表,则可以创建一个n大小的列表,说新列表与旧列表相同(尾部),加上新的元素(头):

val oneSize = new Cons(1, empty)          //> oneSize  : Cons[Int] = Cons@b159eb

如果你检查oneSize的尾部,结果证明它与empty

是同一个对象
oneSize.tail                              //> res0: List[Int] = Nil@1f93f8

让我们使用oneSize列表定义一个包含2个elems的列表:

val twoSize = new Cons(2, oneSize)        //> twoSize  : Cons[Int] = Cons@18654ae

检查尾部我们得到oneSize列表:

twoSize.tail                              //> res1: List[Int] = Cons@b159eb

所以再次使用tail我们必须像以前一样再次获取空列表,确实:

twoSize.tail.tail                         //> res2: List[Int] = Nil@1f93f8

Et瞧,我们刚刚在列表中进行了迭代,就像你的第n个函数一样。