Reputation: 53786
In below Scala method how is the List xs
traversed by method nth
? xs.tail
is called recursively but why is the tail not always the same value since def tail
in trait List
just returns the list of parameterized types?
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")
}
Upvotes: 2
Views: 320
Reputation: 144
You have defined your List type recursively. This means, that you are using another lists for creating new ones. Naturally you have to make the first List somehow, that's why you have defined Nil.
So you can create an empty list without other lists:
val empty = new Nil[Int] //> empty : Nil[Int] = Nil@1f93f8
and you can create not empty lists using already created lists, if you have an n-1 size List, you can create an n size one, saying, that the new list is the same as the old one (tail), plus the new elem (head):
val oneSize = new Cons(1, empty) //> oneSize : Cons[Int] = Cons@b159eb
If you inspect oneSize's tail, it turns out that it is the same object as empty
oneSize.tail //> res0: List[Int] = Nil@1f93f8
Let's define a list with 2 elems, using the oneSize list:
val twoSize = new Cons(2, oneSize) //> twoSize : Cons[Int] = Cons@18654ae
Inspecting the tail we get the oneSize list:
twoSize.tail //> res1: List[Int] = Cons@b159eb
so using tail again we have to get the empty list again as before, and indeed:
twoSize.tail.tail //> res2: List[Int] = Nil@1f93f8
Et voila, we've just iterated through the list, just as your nth function.
Upvotes: 3
Reputation: 67280
The List
is a recursive structure. See the Wikipedia article on Cons. This is from that article:
The structure you would begin with is new Cons(42, new Cons(69, new Cons(613, new Nil)))
. Although the tail
method also returns an instance of List[Int]
, that is not the same list, but the sub-list that follows one of the right-pointing arrows.
So if in your example, you would start with Cons(1, Cons(2, Cons(3, Nil)))
, let n
be 2
.
nth
, we ask: Is Cons(1, Cons(2, Cons(3, Nil)))
empty? No! Is n == 0
? No. So recurse with the tail and n
decremented.Cons(2, Cons(3, Nil))
empty (this is again a List[Int]
)? No. Is n == 0
? No (it's 1
now). Go to the next recursion.Cons(3, Nil)
empty? No. Is n == 0
. Yes! Therefore, return the head of Cons(3, Nil)
which is 3
.Upvotes: 7