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)) end 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 end `````` Kernel.max exists. I should have known better and looked that up rather than trying to roll my own into my function. 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. 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 end `````` 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 ]) end `````` 22 Sep 2013, 20:39 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 (4 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]) end defp get_max(first, second) when first > second do first end defp get_max(_, second), do: second end `````` 09 Jul 2014, 07:09 Patrick Oscity (6 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)) end `````` You must be logged in to comment