Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

tail recursive call in elixir and default parameters

I am writing a simple example in Elixir and although it works I don't really understand how.

defmodule MyList do
  def sum([],acc \\ 0), do: acc
  def sum([head | tail], acc), do: sum(tail,acc + head)
end

When I call MyList.sum I get the expected result

sum([]) => 0
sum([1,2,3]) => 6

I cannot add a default param in the second sum because the compiler throws an error

def sum/2 has default values and multiple clauses, use a separate clause for declaring defaults

So my question is, how come sum([1,2,3]) works? It does not match any of the definitions. Is the function still tail recursive?

like image 814
Batou99 Avatar asked Mar 04 '14 06:03

Batou99


People also ask

What is tail call optimization in Elixir?

- Recursion makes an additional call to call stack (which is why your stack overflows if you forget your base case) - Tail Call Optimizations is a compiler feature that optimizes recursions (if the last thing it does is call itself)! There is no concept of loop in Elixir.

What is tail recursion with example?

What is tail recursion? A recursive function is tail recursive when a recursive call is the last thing executed by the function. For example the following C++ function print() is tail recursive.

How do compilers optimize tail recursive functions?

The idea used by compilers to optimize tail-recursive functions is simple, since the recursive call is the last statement, there is nothing left to do in the current function, so saving the current function’s stack frame is of no use (See this for more details). Can a non-tail recursive function be written as tail-recursive to optimize it?

Is fact a non-tail recursive function?

It is a non-tail-recursive function. Although it looks like a tail recursive at first look. If we take a closer look, we can see that the value returned by fact (n-1) is used in fact (n), so the call to fact (n-1) is not the last thing done by fact (n) The above function can be written as a tail recursive function.


1 Answers

When you have a multiclause with optional arguments, you can specify defaults as a body-less clause:

defmodule MyList do
  def sum(list, acc \\ 0) # sets up default arguments

  def sum([],acc), do: acc
  def sum([head | tail], acc), do: sum(tail,acc + head)
end

Regarding your example, I'm just guessing, but I think that your code amounts to something like following:

defmodule MyList do
  # implicitly generated due to default argument
  def sum(list), do: sum(list, 0)

  def sum([],acc), do: acc
  def sum([head | tail], acc), do: sum(tail,acc + head)
end

Which is why sum([1,2,3]) works as well.

Edit: The function is definitely tail recursive. If the last thing a function does is a call of another function (or itself), then it is a tail call. So in this case, when we call sum(tail, acc + head), first the arguments are calculated, and then a tail recursive call happens.

like image 190
sasajuric Avatar answered Sep 24 '22 15:09

sasajuric