Refactor very remedial recursive loops using pattern matching

I want to know how you would rewrite these two recursive functions using pattern matching, if at all. I’m trying to learn to “think” in Elixir

Simple loop 1

defmodule App do
	def loop(count) do
    IO.inspect count
	  if(count > 0 ) do
		App.loop(count - 1)
	  end
	end
end

App.loop(5)

Simple loop 2

defmodule App do
	def loopList(list) do
		IO.inspect list
		if(length(list) <= 0) do
			nil
		else  

		[head|tail] = list
		App.loopList(tail)
		end
	end
end

App.loopList([5, 4, 3, 2, 1])
1 Like

Hey, I just wrote this quick sketch. Didn’t test it, but it’s just meant to show you that when dealing with recursion, the most idiomatic and common way is to use several function heads to specify the different cases.

In this situation we’ve got two cases, when the count reaches zero and when the count still hasn’t reached zero. So we write a function head for each one, thus separating their logic (which benefits us for debugging and readability) and leverages the possibilities and benefits for pattern matching:

defmodule App do
  def loop(0), do: :ok 
  def loop(count) do
    loop(count - 1)
  end
end

EDIT: in your second example you’re pretty much splitting the different cases but you’re doing everything in the same function. Try to split the different cases in function heads and post the code back and I’ll check it for you if you want!

2 Likes

loop 1:

def loop(0), do: #noop
def loop(n) do
  IO.inspect(n)
  loop(n - 1)
end

loop 2:

def loop_list([]), do: #noop
def loop_list(list = [_head | tail]) do
  IO.inspect list
  loop_list(tail)
end

At least that’s how I’d do it, hope no errors snuck in :slight_smile:

edit: As a more general concept that is, pattern match on the termination case as it is often concrete (empty list, 0, 1 etc.) and do the rest in another function definition.

4 Likes

To avoid an infinite loop on loop(-1), I’d add some guard:

def loop(0), do: #noop
def loop(n) when n > 0 do
  IO.inspect(n)
  loop(n - 1)
end
3 Likes