16 Jul 2013, 02:25

Dave Thomas (338 posts)

  • Write max(list) that returns the element with the maximum value in the list. (This is slightly trickier than it sounds.)
A Possible Solution
# Our solution uses the built-in max/2 function, which 
# returns the larger of its two numeric arguments.
# Although it isn't necessary, we call it as
# `Kernel.max` to avoid confusion

defmodule MyList do
  # max([]) is undefined...

  # max of a single element list is that element
  def max([x]), do: x

  # else recurse
  def max([ head | tail ]), do: Kernel.max(head, max(tail))


IO.puts MyList.max([4])       #=> 4
IO.puts MyList.max([5, 4, 3]) #=> 5
IO.puts MyList.max([4, 5, 3]) #=> 5
IO.puts MyList.max([3, 4, 5]) #=> 5
26 Jul 2013, 15:47

Scott Barron (3 posts)

I see that I have a long way to go to learning to do this stuff elegantly. Here is my initial solution, followed by three important things I learned comparing my code to Dave’s:

defmodule MyList do
  def max([head|tail]), do: _max(tail, head)
  defp _max([], greatest), do: greatest
  defp _max([head|tail], greatest), do: _max(tail, _max(head, greatest))
  defp _max(a, b) when a >= b, do: a
  defp _max(_a, b), do: b

  1. Kernel.max exists. I should have known better and looked that up rather than trying to roll my own into my function.

  2. I don’t always have to use [] as the place I break the recursion when processing lists. In this case [x] makes perfect sense and would eliminate some of the cruft in my answer.

  3. Probably most importantly, I started thinking at the head of the list and what to do with the rest rather than thinking about what things would look like with the recursion “unrolled”. Dave’s code pretty much looks at what would be the last element of the list, calls it the max, and rolls that up. Mine starts with the head, rolling it down, and keeping that messy greatest around for comparison.

This is great stuff, and I love these challenges over just looking at the “right” way of doing it and saying, “Yes, I understand that” without really learning how to arrive at that understanding. Thanks, Dave!

09 Aug 2013, 13:50

Victor Solis (1 post)

Scott, don’t feel bad about rolling your own function. You wouldn’t have learned how it worked, otherwise.

My first stab at this problem actually used Kernel.max. Then I did:

def max([]), do: 0
def max(list), do: Enum.sort(list) |> Enum.reverse |> hd

But I still tried to come up with a version without calling any built in functions because I kinda felt like I was cheating lol.

However, your implementation would result in an error if called with an empty list. It should be something like:

def max(list), do: _max(list, 0)
defp _max([], greatest), do: greatest
defp _max([ head | tail ], greatest) when head > greatest, do: _max(tail, head)
defp _max([ _head| tail ], greatest), do: _max(tail, greatest)

Apologies if you intended it to raise an error with an empty list though.

Dave, might I suggest that the solutions be actual implementations where possible instead of calling existing functions? I think it would help a great deal to see the proper implementations. Great book, btw.

22 Aug 2013, 08:25

Gabe Hollombe (2 posts)

@Scott, my solution was very similar to yours, except I used guard clauses. I agree that @Dave’s is easily better, but when I tackled this exercise my brain was in ‘use what you know’ not ‘look in the docs for help’ mode. =-)

defmodule MyList do
  def max([head|tail]), do: _max(tail, head)
  defp _max([head|tail], e) when head > e, do: _max(tail, head)
  defp _max([head|tail], e) when head <= e, do: _max(tail, e)
  defp _max([], e), do: e
25 Aug 2013, 23:08

Gavin James (1 post)

My solution maintained the maximum value as the head of the list, avoiding the need for an additional function parameter…

defmodule MyList do
  def max([ max ]), do: max
  def max([ max | [head|tail] ]) when max >= head, do: max([ max | tail ])
  def max([ max | [head|tail] ]) when max <  head, do: max([ head | tail ])
22 Sep 2013, 20:39

Antonio Carlos da Graça Mota Durão de Souza (5 posts)

My solution follows @GabeHollombe. For the same reason: ‘use what you know’ not ‘look in the docs for help’

29 Sep 2013, 20:09

Daniel Ashton (7 posts)

Is there a Negative Infinity constant (or concept) in Elixir? With that, I think it could be written in terms of the reduce function that has already been defined, like this:

  def max(list), do: reduce(list, -10_000_000_000, _max(&1, &2))
  defp _max(x, y) when x > y, do: x
  defp _max(_, y), do: y

Just insert the negative infinity constant in place of the negative 10 billion above.

03 Nov 2013, 15:51

Daniel Ashton (7 posts)

It turns out you don’t need a negative infinity concept: just pass in the head value for the first comparison.

Here’s an alternate implementation:

def max(list), do: reduce(list, hd(list), &(&1 > &2 && &1 || &2))
01 Jun 2014, 05:33

Charles F. Munat (5 posts)

def max([head | []]), do: head  
def max([head | tail]), do: Kernel.max(head, max(tail))

Why would you avoid built-in functions? That’s what they’re for. The assignment is to get the max value from a list, not to rewrite the max function.

22 Jun 2014, 06:28

Rodrigo Salado Anaya (2 posts)

Very similar to the others solutions:

defmodule MyList do
  def max([]), do: {:error, "INPUT_ERROR"}
  def max([first]), do: first  
  def max(list) do
    [first | [second | tail]] = list
    max([get_max(first, second) | tail])

  defp get_max(first, second) when first > second do
  defp get_max(_, second), do: second
09 Jul 2014, 07:09

Patrick Oscity (13 posts)

Here is my version, using a guard clause instead of the builtin max function:

defmodule MyList do
  def max([x]), do: x
  def max([x, y]) when x > y, do: x
  def max([x, y]), do: y
  def max([head | tail]), do: max(head, max(tail))
21 Sep 2014, 02:38

lv cheng peng (4 posts)

defmodule Mylist do
  def max([head | []]) do

  def max([max_num | [head | tail]]) when max_num >= head do
    max([max_num | tail])

  def max([max_num | [head | tail]]) when max_num < head do
    max([head | tail])

25 Dec 2014, 00:02

Pierre Sugar (55 posts)

defmodule MyList do
  def max([]),                     do: []
  def max([max]),                  do: max
  def max([a,b|tail]) when a < b,  do: max([b | tail])
  def max([a,_b|tail]),            do: max([a | tail])
14 Mar 2015, 00:15

Larry Prall (1 post)

This is what I used. Not sure where it fits in the hierarchy:

def max([]), do: IO.puts("Undefined.")
def max([head|tail]) when tail != [], do: Kernel.max(head, max(tail))
def max([head|_tail]), do: head
25 Mar 2015, 03:24

Scott Smith (7 posts)

Used an accumulator, then had a hard time getting rid of it.

Lots of interesting solutions above.

defmodule MyList do
  # def max([]) is undefined
  def max([ head | tail ]), do: _max([head | tail], head)
  defp _max([], max_value), do: max_value
  defp _max([ head  | tail ], max_value) when head > max_value, do: _max(tail, head)
  defp _max([ _head | tail ], max_value)                      , do: _max(tail, max_value)
28 Mar 2015, 23:14

John Bohn (1 post)

Rolled my own max as well. Used an accumulator to pull it off.

defmodule MyList do
  def max(list), do: _max(list, 0)
  def _max([], n), do: n
  def _max([head | tail], acc) when head >= acc, do: _max(tail, head)
  def _max([head | tail], acc) when head < acc, do: _max(tail, acc)
  You must be logged in to comment