在下面的Scala方法中,方法xs
如何遍历列表nth
? xs.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")
}
答案 0 :(得分:7)
List
是一个递归结构。请参阅Wikipedia article on Cons。这是来自那篇文章:
您要开始的结构是new Cons(42, new Cons(69, new Cons(613, new Nil)))
。虽然tail
方法也返回List[Int]
的实例,即不是相同的列表,但是跟随其中一个右箭头的子列表。
因此,如果在您的示例中,您将从Cons(1, Cons(2, Cons(3, Nil)))
开始,请n
为2
。
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个函数一样。