16 Jul 2013, 02:25
Dave_gnome_head_isolated_pragsmall

Dave Thomas (337 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
Generic-user-small

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

  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
Generic-user-small

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
91794c7bbbe28cb7e4f624e827ba1f35_pragsmall

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
Avatar_pragsmall

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
Ac_simpsonized_pragsmall

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
Generic-user-small

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
Generic-user-small

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))
  You must be logged in to comment