Reputation: 2100
I have a question on scala tail recursion. I wrote a simple tail recursion code that takes a list and creates a new list of even numbers. But because of scala's inability to append elements to a list, my list is sorted in descending order. Below is the code
def listCreator(lists: List[Int]): List[Int] = {
@tailrec
def evenListCreator(lists: List[Int], accum: List[Int]): List[Int] = {
lists match {
case Nil => accum
case x :: Nil if (isEven (x) == true) => x :: accum
case x :: Nil if (isEven (x) == false) => accum
case x :: tail if (isEven (x) == true) => evenListCreator(tail, x :: accum)
case x :: tail if (isEven (x) == false) => evenListCreator(tail, accum)
}
}
evenListCreator(lists, List())
}
I have the following questions
How can I add a statement that reverses the list within this method?
This line evenListCreator(lists, List())
, which immediately follows the method call, is it mandatory for tail recursion?
Upvotes: 0
Views: 705
Reputation: 51271
Q1. You can reverse the List
before it is returned, as @Mahesh Chand Kandpal has pointed out, or you can build the list with the append method, accum :+ x
, instead of the pre-pend ("cons") method, x :: accum
.
But on a List
the cons method is more efficient than the append method so it is usually better to build-and-reverse.
Q2. No. Tail recursion simply means that there are no other operations waiting after the call returns. In other words, return callMyself()
is a tail-call but return callMyself() + 1
is not.
P.S. I know this is only a learning exercise but, really ...
def listCreator(ints: List[Int]): List[Int] = ints.filter(i => (i&1) < 1)
Upvotes: 2
Reputation: 3250
You can reverse before returning it.
scala> def listCreator(lists: List[Int]): List[Int] = {
| @tailrec
| def evenListCreator(lists: List[Int], accum: List[Int]): List[Int] = {
| lists match {
| case Nil => accum
| case x :: Nil if (isEven (x) == true) => x :: accum
| case x :: Nil if (isEven (x) == false) => accum
| case x :: tail if (isEven (x) == true) => evenListCreator(tail, x :: accum)
| case x :: tail if (isEven (x) == false) => evenListCreator(tail, accum)
| }
| }
| evenListCreator(lists, List.empty[Int]).reverse
| }
listCreator: (lists: List[Int])List[Int]
scala> listCreator((1 to 10).toList)
res2: List[Int] = List(2, 4, 6, 8, 10)
scala>
You don't need to immediately follow the method call but if you don't do you need to send two arguments one is list and second is empty list. So we take only list of integers so that one who is using, has not to bother like sending empty list also.
And You can do it directly also
scala> val list = (1 to 10).toList
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> list.map(_ * 2)
res8: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
scala>
Upvotes: 2
Reputation: 3514
evenListCreator(lists, List()).reverse
or evenListCreator(lists.reverse, List())
, but in your case the first form is better as the list to reverse will most likely be shorter after the call to evenListCreator
evenListCreator(lists, List())
doesn't follow the method call, it is the method call. Without it, nothing would happen as you would only define your tail recursive function (def evenListCreator
) and return without calling it.Some other notes
You have too many stop conditions, this is enough:
@tailrec
def evenListCreator(lists: List[Int], accum: List[Int]): List[Int] = {
lists match {
case Nil => accum
case x :: tail if (isEven (x) == true) => evenListCreator(tail, x :: accum)
case x :: tail if (isEven (x) == false) => evenListCreator(tail, accum)
}
}
Code is too verbose, I think this is better:
@tailrec
def evenListCreator(lists: List[Int], accum: List[Int]): List[Int] = {
lists match {
case Nil => accum
case x :: tail if isEven (x) => evenListCreator(tail, x :: accum)
case x :: tail if !isEven (x) => evenListCreator(tail, accum)
}
}
You can also call the recursive function that way:
evenListCreator(lists, Nil)
Upvotes: 2