10

I'm trying to port this haskell max function implementation to scala

maximum' :: (Ord a) => [a] -> a  
maximum' [] = error "maximum of empty list"  
maximum' [x] = x  
maximum' (x:xs) = max x (maximum' xs) 

This is my first attempt:

def max[T <: Ordered[T]](list: List[T]): T = list match {

  case Nil => throw new Error("maximum of empty list")
  case head :: Nil => head
  case list => {
    val maxTail = max(list.tail)
    if (list.head > maxTail) list.head else maxTail
  }
}


max(List[Int](3,4))

But I get the following error:

inferred type arguments [Int] do not conform to method max's type parameter bounds [T <: Ordered[T]]

I tried with ordering, comprable, etc with similar results...

Any idea about what's missing?

9 Answers 9

21

Went through a similar exercise as the OP sans pattern matching and generic types, and came up with the following:

def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new NoSuchElementException

    if (xs.length == 1) 
        return xs.head 
      else 
        return max(xs.head, max(xs.tail))
  }

  def max(x: Int, y: Int): Int = if (x > y) x else y
Sign up to request clarification or add additional context in comments.

1 Comment

> return max(xs.head, max(xs.tail)) You can not make this call, as you don't have any function max(x: Int, xs: List[Int]) defined.
18

Maybe you want the Ordering type class?

def max[T: Ordering](list: List[T]): T = list match {
  case Nil => throw new RuntimeException("maximum of empty list")
  case head :: Nil => head
  case list =>
    val maxTail = max(list.tail)
    if (implicitly[Ordering[T]].gt(list.head, maxTail)) list.head else maxTail
}

This is, after all, how the built-in max method works:

// From GenTraversableOnce
def max[A1 >: A](implicit ord: Ordering[A1]): A

You can clean things up a lot if you do this:

def max[T](list: List[T])(implicit ord: Ordering[T]): T = list match {
  case Nil => throw new RuntimeException("maximum of empty list")
  case head :: Nil => head
  case head :: tail => ord.max(head, max(tail))
}

Or, you can make it tail-recursive for increased efficiency (because the compiler will optimize it):

def max[T](list: List[T])(implicit ord: Ordering[T]): T = {
  if (list.isEmpty)
    throw new RuntimeException("maximum of empty list")

  @tailrec
  def inner(list: List[T], currMax: T): T =
    list match {
      case Nil => currMax
      case head :: tail => inner(tail, ord.max(head, currMax))
    }
  inner(list.tail, list.head)
}

Also, you should throw RuntimeException or a subclass of it, not Error.

3 Comments

+1 for having a look at the source... (I still don't have enough courage... use the source!!!)
I guess using ord.max would be like cheating, I'm doing it just for educational purposes... anyway, I created a maxElement helper function to avoid that val in there...
ord.max is different from List.max. One is basically an operator that compares two things while the other is a method of an entire collection. They just happen to have the same name.
6

I have just come up with this solution.

def max(xs: List[Int]): Int = {
    if (xs.isEmpty) 0
    else {
      if( xs.head >= max(xs.tail) ) xs.head
      else max(xs.tail)
    }
}

4 Comments

I think this is not a generic solution
@opensas No, but I wonted to attach it to the most relevant question, currently this is the one.
@eomeroff Close, but your solution doesn't work for negative numbers (try max(List(-3, -7, -2)). This can be resolved by modifying the second if to if( xs.tail.isEmpty || xs.head >= max(xs.tail) ).
@eomeroff No probs, perhaps you can update your answer to help somebody else. You should also throw an exception if the list is empty, as 0 is incorrect (max can also be negative).
2

I came up with quite a simple solution which is easy to understand. It caters for an empty list, a list with only one element, and negative numbers.

def max(xs: List[Int]): Int = {

  if (xs.isEmpty) throw new NoSuchElementException
  else {
    def inner(max: Int, list: List[Int]): Int = {

      def compare(x: Int, y: Int): Int =
        if (x > y) x
        else y

      if (list.isEmpty) max
      else inner(compare(max, list.head), list.tail)
    }
    inner(xs.head, xs.tail)
  } 
}

Comments

0

Oops, shoulda look better before asking

I found the answer in this thread: https://stackoverflow.com/a/691674/47633

It seems like Haskell's type classes are implemented using implicits in scala (like in dhg's example)

so it ends up like this:

def max[T](list: List[T])(implicit f: T => Ordered[T]): T = {

 def maxElement(value1: T, value2: T): T = if (value1 > value2) value1 else value2

 list match {
    case Nil => throw new Error("empty list found")
    case head :: Nil => head
    case list => maxElement(list.head, max(list.tail))
  }
} 

or with some syntactic sugar, just

def max[T <% Ordered[T]](list: List[T]): T = list match {

Still, I think the compiler has enough information to do it by himself...

ps: I prettied up a little bit the function...

Comments

0
def genFunc[A](a1: A, a2: A)(f:(A, A) => Boolean):A = if (f(a1, a2)) a1 else a2
def min[A : Ordering] = (a1: A, a2: A) => implicitly[Ordering[A]].lt(a1, a2)
def max[A : Ordering] = (a1: A, a2: A) => implicitly[Ordering[A]].gt(a1, a2)

List(1,2,8,3,4,6,0).reduce(genFunc(_,_)(min))
List(1,2,8,3,4,6,0).reduce(genFunc(_,_)(max))

or if need only function max with tail recursion and the type Option[_] does not break the referential transparency

def max[A: Ordering](list: List[A]): Option[A] = list match {
  case Nil => None
  case h :: Nil => Some(h)
  case h :: t => if(implicitly[Ordering[A]].gt(h, t.head)) max(h :: t.tail) else max(t)
}

max(List(1,2,8,3,4,6,0)).getOrElse("List is empty") // 8: Any
max(List()).getOrElse("List is empty")              // List is empty: Any

Comments

0

this is the simplest way I could come up with:

def max(xs: List[Int]): Int = { if (xs.length == 1) xs.head

else if(xs.isEmpty) throw new NoSuchElementException

else if(xs.head > max(xs.tail))  xs.head

else max(xs.tail)}  }

1 Comment

Relatively simple, true, but massively inefficient. A small List of only 7 elements, with the max element at the end, will recurse 126 times. Change it to 8 elements and it recurses 254 times, approximately doubling for each (smaller) element added (before the end).
0

this works

def max(xs: List[Int]): Int = xs match{
  case Nil => 0
  case head :: Nil => head
  case head :: tail => max(List(head, max(tail)))
}

1 Comment

Unfortunately this is not generic, as requested in the title, and it doesn't work. For lists of two or more elements it is infinitely recursive.
-1

My solve:

  def max(xs: List[Int]): Int = {
    if (xs.isEmpty) 0
    else xs.max
  }

1 Comment

This isn't recursive, which is what was asked for (6 years ago).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.