Find max value in a list recursively in scala - scala

I'm new to Scala, there is a better way to express this with the most basic knowledge possible?
def findMax(xs: List[Int]): Int = {
xs match {
case x :: tail => (if (tail.length==0) x else (if(x>findMax(tail)) x else (findMax(tail))))
}
}

Thee are two problems here. First, you call tail.length which is an operation of order O(N), so in the worst case this will cost you N*N steps where N is the length of the sequence. The second is that your function is not tail-recursive - you nest the findMax calls "from outside to inside".
The usual strategy to write the correct recursive function is
to think about each possible pattern case: here you have either the empty list Nil or the non-empty list head :: tail. This solves your first problem.
to carry along the temporary result (here the current guess of the maximum value) as another argument of the function. This solves your second problem.
This gives:
import scala.annotation.tailrec
#tailrec
def findMax(xs: List[Int], max: Int): Int = xs match {
case head :: tail => findMax(tail, if (head > max) head else max)
case Nil => max
}
val z = util.Random.shuffle(1 to 100 toList)
assert(findMax(z, Int.MinValue) == 100)
If you don't want to expose this additional argument, you can write an auxiliary inner function.
def findMax(xs: List[Int]): Int = {
#tailrec
def loop(ys: List[Int], max: Int): Int = ys match {
case head :: tail => loop(tail, if (head > max) head else max)
case Nil => max
}
loop(xs, Int.MinValue)
}
val z = util.Random.shuffle(1 to 100 toList)
assert(findMax(z) == 100)
For simplicity we return Int.MinValue if the list is empty. A better solution might be to throw an exception for this case.
The #tailrec annotation here is optional, it simply assures that we indeed defined a tail recursive function. This has the advantage that we cannot produce a stack overflow if the list is extremely long.

Any time you're reducing a collection to a single value, consider using one of the fold functions instead of explicit recursion.
List(3,7,1).fold(Int.MinValue)(Math.max)
// 7

Even I too am new to Scala (am into Haskell though!).
My attempt at this would be as below.
Note that I assume a non-empty list, since the max of an empty list does not make sense.
I first define an helper method which simply returns the max of 2 numbers.
def maxOf2 (x:Int, y:Int): Int = {
if (x >= y) x
else y
}
Armed with this simple function, we can build a recursive function to find the 'max' as below:
def findMax(xs: List[Int]): Int = {
if (xs.tail.isEmpty)
xs.head
else
maxOf2(xs.head, findMax(xs.tail))
}
I feel this is a pretty 'clear'(though not 'efficient') way to do it.
I wanted to make the concept of recursion obvious.
Hope this helps!

Elaborating on #fritz's answer. If you pass in an empty list, it will throw you a java.lang.UnsupportedOperationException: tail of empty list
So, keeping the algorithm intact, I made this adjustment:
def max(xs: List[Int]): Int = {
def maxOfTwo(x: Int, y: Int): Int = {
if (x >= y) x else y
}
if (xs.isEmpty) throw new UnsupportedOperationException("What man?")
else if (xs.size == 1) xs.head
else maxOfTwo(xs.head, max(xs.tail))
}
#fritz Thanks for the answer

Using pattern matching an recursion,
def top(xs: List[Int]): Int = xs match {
case Nil => sys.error("no max in empty list")
case x :: Nil => x
case x :: xs => math.max(x, top(xs))
}
Pattern matching is used to decompose the list into head and rest. A single element list is denoted with x :: Nil. We recurse on the rest of the list and compare for maximum on the head item of the list at each recursive stage. To make the cases exhaustive (to make a well-defined function) we consider also empty lists (Nil).

def maxl(xl: List[Int]): Int = {
if ( (xl.head > xl.tail.head) && (xl.tail.length >= 1) )
return xl.head
else
if(xl.tail.length == 1)
xl.tail.head
else
maxl(xl.tail)
}

Related

Recursive sorting in Scala including an exit condition

I am attempting to sort a List of names using Scala, and I am trying to learn how to do this recursively. The List is a List of Lists, with the "element" list containing two items (lastName, firstName). My goal is to understand how to use recursion to sort the names. For the purpose of this post my goal is just to sort by the length of lastName.
If I call my function several times on a small sample list, it will successfully sort lastName by length from shortest to longest, but I have not been able to construct a satisfactory exit condition using recursion. I have tried variations of foreach and other loops, but I have been unsuccessful. Without a satisfactory exit condition, the recursion just continues forever.
import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec
val nameListBuffer = new ListBuffer[List[String]]
val source = Source.fromFile("shortnames.txt")
val lines = source.getLines()
for (line <- lines) {
nameListBuffer += line.split(" ").reverse.toList
}
#tailrec
def sorting(x: ListBuffer[List[String]]): Unit = {
for (i <- 0 until ((x.length)-1)) {
var temp = x(i)
if (x(i)(0).length > x(i+1)(0).length) {
x(i) = x(i+1)
x(i+1) = temp
}
}
var continue = false
while (continue == false) {
for (i <- 0 until ((x.length)-1)) {
if (x(i)(0).length <= x(i+1)(0).length) {
continue == false//want to check ALL i's before evaluating
}
else continue == true
}
}
sorting(x)
}
sorting(nameListBuffer)
Sorry about the runtime complexity it's basically an inefficient bubble sort at O(n^4) but the exit criteria - focus on that. For tail recursion, the key is that the recursive call is to a smaller element than the preceding recursive call. Also, keep two arguments, one is the original list, and one is the list that you are accumulating (or whatever you want to return, it doesn't have to be a list). The recursive call keeps getting smaller until eventually you can return what you have accumulated. Use pattern matching to catch when the recursion has ended, and then you return what you were accumulating. This is why Lists are so popular in Scala, because of the Nil and Cons subtypes and because of operators like the :: can be handled nicely with pattern matching. One more thing, to be tail recursive, the last case has to make a recursive or it won't run.
import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec
// I did not ingest from file I just created the test list from some literals
val dummyNameList = List(
List("Johanson", "John"), List("Nim", "Bryan"), List("Mack", "Craig")
, List("Youngs", "Daniel"), List("Williamson", "Zion"), List("Rodgersdorf", "Aaron"))
// You can use this code to populate nameList though I didn't run this code
val source = Source.fromFile("shortnames.txt")
val lines = source.getLines()
val nameList = {
for (line <- lines) yield line.split(" ").reverse.toList
}.toList
println("\nsorted:")
sortedNameList.foreach(println(_))
//This take one element and it will return the lowest element from the list
//of the other argument.
#tailrec
private def swapElem(elem: List[String], listOfLists: List[List[String]]): List[String] = listOfLists match {
case Nil => elem
case h::t if (elem(0).length > h(0).length) => swapElem(h, t)
case h::t => swapElem(elem, t)
}
//If the head is not the smallest element, then swap out the element
//with the smallest element of the list. I probably could have returned
// a tuple it might have looked nicer. It just keeps iterating though until
// there is no elements
#tailrec
private def iterate(listOfLists: List[List[String]], acc: List[List[String]]): List[List[String]] = listOfLists match {
case h::Nil => acc :+ h
case h::t if (swapElem(h, t) != h) => iterate(h :: t.filterNot(_ == swapElem(h, t)), acc :+ swapElem(h, t))
case h::t => iterate(t, acc :+ swapElem(h, t))
}
val sortedNameList = iterate(nameList, List.empty[List[String]])
println("\nsorted:")
sortedNameList.foreach(println(_))
sorted:
List(Nim, Bryan)
List(Mack, Craig)
List(Youngs, Daniel)
List(Johanson, John)
List(Williamson, Zion)
List(Rodgersdorf, Aaron)

Add an element(Int,Double...) at the end of an "Any" type list

I'm trying to add an element at the end of "Any" type list ( List[Any] ).
I want to use a recursive function to build it the idea is "if I need of this element I'll append it, when the iteration is over my list will be complete".
In the following code the idea is "I have a List 'l' if the element 'elem' is at the head of 'l' I'll add its position, saved in 'index', as next element of the list 'ret'. Otherwise I'll check the next element and doing nothing (I'm using 'l :: support...' only to match the return kind as List[Any]). When 'l' is empty(or Nil) give me the list 'ret'.
At the end 'ret' is the list that contains the position of all the elements 'elem' found in the list 'l'".
This is very important : the list that I'm building is 'ret' in fact it is the return of recursive function!
I tried with "::","+:",":+" but they didn't work. The error was the same every time "error: value :: is not a member of type parameter Any".
object find{
def support[Any](l:List[Any],elem:Any,index:Int,ret:List[Any]):List[Any]={
if (l==Nil) ret;
else if(l.head==elem) (l :: support(l.tail,elem,index+1,ret :: (index.asInstanceOf[Any])));
else (l :: support(l.tail,elem,index+1,ret));
}
def findIndices[Any](l:List[Any],x:Any):List[Any]={//I want to find all the elements "elem" in the list "l"
val a:List[Any]=support(l,x,0,List[Any]());//I'll build my list using an auxiliary function "support"
a;
}
}
object main extends App{
val l:List[Int]=List(1,2,2,2,2,3,4,5)
val x:Int=2;
val f:List[Int]=find.findIndices(l,x)
println(f)
}
I'm opened to all the working solutions but please first try to respond to my question and explain how you're doing it and why.
I'm learning this language, I came from C and Java.
TL;DR One Line Solution:
val list = List(1,2,2,2,2,3,4,5)
list.zipWithIndex.filter(_._1 == 2).map(_._2) // List(1, 2, 3, 4)
Or name your variables with case notation:
list.zipWithIndex.filter { case(value,index) => value == 2 } map { case(value,index) => index }
Or use the collect method to combine filter and map:
list.zipWithIndex.collect { case (value,index) if value == 2 => index }
Recursion
If you really need to use recursion, there's an easy way and a hard way to do this, and it looks like you're trying to do it the hard way. The hard way makes sense in this case, but I'll do it the easy way first to help show you what I'm doing and why.
So given a List
val list = List(1,2,2,2,2,3,4,5) // named list instead of l because l looks like 1.
We want a function findIndices such that findIndices(list,2) returns List(1,2,3,4)
I'm going to start by defining findIndices like this:
def findIndices(list: List[Any], element: Any): List[Any] = ???
Now there's a couple of things I want to change right away. First, your example uses a list of things with a single type, so this looks like a great opportunity to use a parameterized type.
def findIndices[T](list: List[T], element: T): List[Any] = ???
Second thing is that no matter what is inside the list, the resulting list of indices will be integers because indices are integers.
def findIndices[T](list: List[T], element: T): List[Int] = ???
So now I'm ready to work on the body of the method. I know this needs to be recursive, and the general form of a recursive function is:
check for terminating case.
handle non terminating cases.
So my function is going to look something like this:
def findIndices[T](list: List[T], element: T): List[Int] = {
// if (terminating case) return termination
// else if (check for next index of element) return index plus recursive call
// else return recursive call
}
Filling in the blanks gives us something like this:
def findIndices[T](list: List[T], element: T): List[Int] = {
if (list.isEmpty) List.empty[Int]
else if (list.head == element) 0 :: findIndices(list.tail, element)
else findIndices(list.tail, element)
}
Unfortunately this code has a bug. We're calculating the indices based on the ever shortening list, instead of the original list. We can fix this by keeping track of the offset of the indices as we use shorter and shorter versions of the list:
def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {
if (list.isEmpty) List.empty[Int]
else if (list.head == element) offset :: findIndices(list.tail, element, offset + 1)
else findIndices(list.tail, element, offset + 1)
}
This method works as intended...for small lists only. For very large lists, we will get a stack overflow. The way to fix this is to make the method tail-recursive, so the program doesn't need to keep track of the stack as it makes each call. This is what you seem to be trying to do in your question. I called it the hard way, but once you have the non-tail recursive function, converting it to the tail recursive function is actually fairly straightforward and mechanical.
The top level function parameters remains the same:
def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = ???
Inside the first function we define a new function. This new function will have the same parameters as the old one, plus an accumulator. This accumulator will allow us to pass the intermediate results of the algorithm down the stack to the next recursive function call, so the program won't have to maintain the call stack to keep track of every intermediate result.
The only thing our outer function will do is call the inner function with the initial parameters.
def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {
def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
// do logic here
}
findIndicesAcc(list, element, List.empty[Int])
}
The logic inside the accumulator function will be very similiar to the original function. It will simply take advantage of the accumulator parameter instead of leaving the intermediate result on the stack.
def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {
def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
if (list.isEmpty) acc
else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
else findIndicesAcc(list.tail, element, offset + 1, acc)
}
findIndicesAcc(list, element, List.empty[Int])
}
This function works as intended, but there are a couple of final bookkeeping and clean up items we can do. First, we can get rid of the outermost offset parameter and take away the inner offset parameter's default value. Second, we can add a #tailrec annotation to the inner function, to ensure that we've solved the stack overflow problem. Third, if the order of the indices in the returned list is important, we can call reverse on the output of the accumulator.
import scala.annotation.tailrec
def findIndices[T](list: List[T], element: T): List[Int] = {
#tailrec
def findIndicesAcc(list: List[T], element: T, offset: Int, acc: List[Int]): List[Int] = {
if (list.isEmpty) acc
else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
else findIndicesAcc(list.tail, element, offset + 1, acc)
}
findIndicesAcc(list, element, 0, List.empty[Int]).reverse
}
More About Tail Recursion
Tail recursion refers to a recursive function where the recursive call is the last thing that happens in the function. Sometimes you have to look closely to see if something is tail-recursive or not. For example, the code fragment
else if (list.head == element) offset :: findIndices(list.tail, element, offset + 1)
is not tail recursive because offset has to be prepended to the results of findIndices after it is returned. This can be made more clear if we break each operation of the code fragment out into separate lines:
else if (list.head == element) {
val tail = list.tail
val newOffset = offset + 1
val recursiveResult = findIndices(tail, element, newOffset)
return offset :: recursiveResult
}
When the code is broken down like this, we can see that more work has to be done after the recursive findIndices call returns.
On the other hand, the code fragment
else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
is tail-recursive. When we break the operations out into separate lines we get
else if (list.head == element) {
val tail = list.tail
val newOffset = offset + 1
val combinedList = offset :: acc
return findIndicesAcc(tail, element, newOffset, combinedList)
}
and we can clearly see that the findIndicesAcc call is the last thing that happens.
In the first case, the program is forced to maintain the entire call stack, because it needs to remember what the value of offset was in each prior iteration of the function. In linux machines, we typically have 8 MB of stack to use. For very long lists, we end up using up all 8 MB of the stack and this results in a stack overflow exception.
In the second case, all the relevant data is passed to the next iteration of the recursive function. There is nothing in previous function call that the program needs to keep track of. The compiler is able to detect this, and optimize the code into basically a loop. In this case, there is no call stack to maintain, and so no risk of a stack overflow exception.
One last caveat, I checked this code as closely as I could, but I did not have access to a scala compiler when I wrote this, so I apologize for any typos.
I think you should learn a bit more about generics in Scala. Particularly the idea of calling the generic parameter Any so it shadows the standard type Any is a very bad idea. Also it might help learning about pattern matching which is a powerful tool to replace some if/else if/else statements. I believe the code you want would looks like this:
object find {
#tailrec
def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
case Nil => ret
case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
// _ meas here we ignore head anyway so don't need a variable for that
case _ :: tail => support(tail, elem, index + 1, ret)
}
def findIndices[A](l: List[A], x: A): List[Int] = {
//I want to find all the elements "elem" in the list "l"
support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
}
}
You can try this code online here
Note that I made some improvements but there a few more improvements possible. For example, typically such support method would be put inside the findIndices so it is not available from the outside. Also :+ is very slow on Lists. Typically it is faster to build the result list by appending to the start and then reverse it once at the very end.
Fully handmade solution
def reverse[T](list: List[T]): List[T] = {
#annotation.tailrec
def loop(remaining: List[T], acc: List[T]): List[T] = remaining match {
case Nil => acc
case x :: xs => loop(remaining = xs, acc = x :: acc)
}
loop(remaining = list, acc = Nil)
}
def findIndices[T](elem: T, list: List[T]): List[Int] = {
#annotation.tailrec
def loop(remaining: List[T], acc: List[Int], currentIndex: Int): List[Int] = remaining match {
case Nil => acc
case x :: xs =>
val newAcc = if (x == elem) currentIndex :: acc else acc
loop(remaining = xs, newAcc, currentIndex + 1)
}
reverse(loop(remaining = list, acc = Nil, currentIndex = 0))
}
Using standar higher order functions
def findIndicesStd[T](elem: T, list: List[T]): List[Int] =
list.zipWithIndex.filter(_._1 == elem).map(_._2)
Results
findIndices(elem = 5, list = List(1, 5, 3, 5, 8, 5, 5, 10))
// res0: List[Int] = List(1, 3, 5, 6)
findIndicesStd(elem = 5, list = List(1, 5, 3, 5, 8, 5, 5, 10))
// res1: List[Int] = List(1, 3, 5, 6)
Explanation
Feel free to ask as many questions as you want, and I will edit this section with the answers.
However, I will answer a few ones I believe you will have now:
What does #annotation.tailrec mean?: In the code, nothing really. It is a compiler annotation which tells the compiler to check if the given function is recursive-by-tail, and if not emit a warning - it is more like a best practice to make sure the function will not blow up the stack.
Why T instead of Any?: T here means a generic, it is like a type placeholder saying, this works for "Any" type. Any on the other hand is a concrete type (the super type of everything). They look similar, but the generic ensures you don't lose type information (if you have a List of Ints and you reverse it, it will get a List of Int, not of Any, back).
Why reversing the results?: Lists are great for prepending, but "horrible" for prepending - in general is always better to iterate them twice one for transforming and the other for reversing (Unless you use mutations like the internal implementations of List does... but that may break your program if you don't do it with care).
What does _._1 & _._2 mean?: The first underscore is used for anonymous functions, it means the first (and in this case only) parameter, _1 & _2 are methods (as you can see because they are being called with a dot .) on the Tuple class, they access the first and second elements of the tuple.

Accumulate result until some condition is met in functional way

I have some expensive computation in a loop, and I need to find max value produced by the calculations, though if, say, it will equal to LIMIT I'd like to stop the calculation and return my accumulator.
It may easily be done by recursion:
val list: List[Int] = ???
val UpperBound = ???
def findMax(ls: List[Int], max: Int): Int = ls match {
case h :: rest =>
val v = expensiveComputation(h)
if (v == UpperBound) v
else findMax(rest, math.max(max, v))
case _ => max
}
findMax(list, 0)
My question: whether this behaviour template has a name and reflected in scala collection library?
Update: Do something up to N times or until condition is met in Scala - There is an interesting idea (using laziness and find or exists at the end) but it is not directly applicable to my particular case or requires mutable var to track accumulator.
I think your recursive function is quite nice, so honestly I wouldn't change that, but here's a way to use the collections library:
list.foldLeft(0) {
case (max, next) =>
if(max == UpperBound)
max
else
math.max(expensiveComputation(next), max)
}
It will iterate over the whole list, but after it has hit the upper bound it won't perform the expensive computation.
Update
Based on your comment I tried adapting foldLeft a bit, based on LinearSeqOptimized's foldLeft implementation.
def foldLeftWithExit[A, B](list: Seq[A])(z: B)(exit: B => Boolean)(f: (B, A) => B): B = {
var acc = z
var remaining = list
while (!remaining.isEmpty && !exit(acc)) {
acc = f(acc, list.head)
remaining = remaining.tail
}
acc
}
Calling it:
foldLeftWithExit(list)(0)(UpperBound==){
case (max, next) => math.max(expensiveComputation(next), max)
}
You could potentially use implicits to omit the first parameter of list.
Hope this helps.

Idiomatic "do until" collection updating

Scenario:
val col: IndexedSeq[Array[Char]] = for (i <- 1 to n) yield {
val x = for (j <- 1 to m) yield 'x'
x.toArray
}
This is a fairly simple char matrix. toArray used to allow updating.
var west = last.x - 1
while (west >= 0 && arr(last.y)(west) == '.') {
arr(last.y)(west) = ch;
west -= 1;
}
This is updating all . to ch until a non-dot char is found.
Generically, update until stop condition is met, unknown number of steps.
What is the idiomatic equivalent of it?
Conclusion
It's doable, but the trade-off isn't worth it, a lot of performance is lost to expressive syntax when the collection allows updating.
Your wish for a "cleaner, more idiomatic" solution is of course a little fuzzy, because it leaves a lot of room for subjectivity. In general, I'd consider a tail-recursive updating routine more idiomatic, but it might not be "cleaner" if you're more familiar with a non-functional programming style. I came up with this:
#tailrec
def update(arr:List[Char], replace:Char, replacement:Char, result:List[Char] = Nil):List[Char] = arr match {
case `replace` :: tail =>
update(tail, replace, replacement, replacement :: result)
case _ => result.reverse ::: arr
}
This takes one of the inner sequences (assuming a List for easier pattern matching, since Arrays are trivially convertible to lists), and replaces the replace char with the replacement recursively.
You can then use map to update the outer sequence, like so:
col.map { x => update(x, '.', ch) }
Another more reusable alternative is writing your own mapUntil, or using one which is implemented in a supplemental library (Scalaz probably has something like it). The one I came up with looks like this:
def mapUntil[T](input:List[T])(f:(T => Option[T])) = {
#tailrec
def inner(xs:List[T], result:List[T]):List[T] = xs match {
case Nil => Nil
case head :: tail => f(head) match {
case None => (head :: result).reverse ::: tail
case Some(x) => inner(tail, x :: result)
}
}
inner(input, Nil)
}
It does the same as a regular map invocation, except that it stops as soon as the passed function returns None, e.g.
mapUntil(List(1,2,3,4)) {
case x if x >= 3 => None
case x => Some(x-1)
}
Will result in
List[Int] = List(0, 1, 3, 4)
If you want to look at Scalaz, this answer might be a good place to start.
x3ro's answer is the right answer, esp. if you care about performance or are going to be using this operation in multiple places. I would like to add simple solution using only what you find in the collections API:
col.map { a =>
val (l, r) = a.span(_ == '.')
l.map {
case '.' => ch
case x => x
} ++ r
}

Why doesn't my recursive function return the max value of a List

I have the following recursive function in Scala that should return the maximum size integer in the List. Is anyone able to tell me why the largest value is not returned?
def max(xs: List[Int]): Int = {
var largest = xs.head
println("largest: " + largest)
if (!xs.tail.isEmpty) {
var next = xs.tail.head
println("next: " + next)
largest = if (largest > next) largest else next
var remaining = List[Int]()
remaining = largest :: xs.tail.tail
println("remaining: " + remaining)
max(remaining)
}
return largest
}
Print out statements show me that I've successfully managed to bring back the largest value in the List as the head (which was what I wanted) but the function still returns back the original head in the list. I'm guessing this is because the reference for xs is still referring to the original xs list, problem is I can't override that because it's a val.
Any ideas what I'm doing wrong?
You should use the return value of the inner call to max and compare that to the local largest value.
Something like the following (removed println just for readability):
def max(xs: List[Int]): Int = {
var largest = xs.head
if (!xs.tail.isEmpty) {
var remaining = List[Int]()
remaining = largest :: xs.tail
var next = max(remaining)
largest = if (largest > next) largest else next
}
return largest
}
Bye.
I have an answer to your question but first...
This is the most minimal recursive implementation of max I've ever been able to think up:
def max(xs: List[Int]): Option[Int] = xs match {
case Nil => None
case List(x: Int) => Some(x)
case x :: y :: rest => max( (if (x > y) x else y) :: rest )
}
(OK, my original version was ever so slightly more minimal but I wrote that in Scheme which doesn't have Option or type safety etc.) It doesn't need an accumulator or a local helper function because it compares the first two items on the list and discards the smaller, a process which - performed recursively - inevitably leaves you with a list of just one element which must be bigger than all the rest.
OK, why your original solution doesn't work... It's quite simple: you do nothing with the return value from the recursive call to max. All you had to do was change the line
max(remaining)
to
largest = max(remaining)
and your function would work. It wouldn't be the prettiest solution, but it would work. As it is, your code looks as if it assumes that changing the value of largest inside the recursive call will change it in the outside context from which it was called. But each new call to max creates a completely new version of largest which only exists inside that new iteration of the function. Your code then throws away the return value from max(remaining) and returns the original value of largest, which hasn't changed.
Another way to solve this would have been to use a local (inner) function after declaring var largest. That would have looked like this:
def max(xs: List[Int]): Int = {
var largest = xs.head
def loop(ys: List[Int]) {
if (!ys.isEmpty) {
var next = ys.head
largest = if (largest > next) largest else next
loop(ys.tail)
}
}
loop(xs.tail)
return largest
}
Generally, though, it is better to have recursive functions be entirely self-contained (that is, not to look at or change external variables but only at their input) and to return a meaningful value.
When writing a recursive solution of this kind, it often helps to think in reverse. Think first about what things are going to look like when you get to the end of the list. What is the exit condition? What will things look like and where will I find the value to return?
If you do this, then the case which you use to exit the recursive function (by returning a simple value rather than making another recursive call) is usually very simple. The other case matches just need to deal with a) invalid input and b) what to do if you are not yet at the end. a) is usually simple and b) can usually be broken down into just a few different situations, each with a simple thing to do before making another recursive call.
If you look at my solution, you'll see that the first case deals with invalid input, the second is my exit condition and the third is "what to do if we're not at the end".
In many other recursive solutions, Nil is the natural end of the recursion.
This is the point at which I (as always) recommend reading The Little Schemer. It teaches you recursion (and basic Scheme) at the same time (both of which are very good things to learn).
It has been pointed out that Scala has some powerful functions which can help you avoid recursion (or hide the messy details of it), but to use them well you really do need to understand how recursion works.
The following is a typical way to solve this sort of problem. It uses an inner tail-recursive function that includes an extra "accumulator" value, which in this case will hold the largest value found so far:
def max(xs: List[Int]): Int = {
def go(xs: List[Int], acc: Int): Int = xs match {
case Nil => acc // We've emptied the list, so just return the final result
case x :: rest => if (acc > x) go(rest, acc) else go(rest, x) // Keep going, with remaining list and updated largest-value-so-far
}
go(xs, Int.MinValue)
}
Nevermind I've resolved the issue...
I finally came up with:
def max(xs: List[Int]): Int = {
var largest = 0
var remaining = List[Int]()
if (!xs.isEmpty) {
largest = xs.head
if (!xs.tail.isEmpty) {
var next = xs.tail.head
largest = if (largest > next) largest else next
remaining = largest :: xs.tail.tail
}
}
if (!remaining.tail.isEmpty) max(remaining) else xs.head
}
Kinda glad we have loops - this is an excessively complicated solution and hard to get your head around in my opinion. I resolved the problem by making sure the recursive call was the last statement in the function either that or xs.head is returned as the result if there isn't a second member in the array.
The most concise but also clear version I have ever seen is this:
def max(xs: List[Int]): Int = {
def maxIter(a: Int, xs: List[Int]): Int = {
if (xs.isEmpty) a
else a max maxIter(xs.head, xs.tail)
}
maxIter(xs.head, xs.tail)
}
This has been adapted from the solutions to a homework on the Scala official Corusera course: https://github.com/purlin/Coursera-Scala/blob/master/src/example/Lists.scala
but here I use the rich operator max to return the largest of its two operands. This saves having to redefine this function within the def max block.
What about this?
def max(xs: List[Int]): Int = {
maxRecursive(xs, 0)
}
def maxRecursive(xs: List[Int], max: Int): Int = {
if(xs.head > max && ! xs.isEmpty)
maxRecursive(xs.tail, xs.head)
else
max
}
What about this one ?
def max(xs: List[Int]): Int = {
var largest = xs.head
if( !xs.tail.isEmpty ) {
if(xs.head < max(xs.tail)) largest = max(xs.tail)
}
largest
}
My answer is using recursion is,
def max(xs: List[Int]): Int =
xs match {
case Nil => throw new NoSuchElementException("empty list is not allowed")
case head :: Nil => head
case head :: tail =>
if (head >= tail.head)
if (tail.length > 1)
max(head :: tail.tail)
else
head
else
max(tail)
}
}