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?
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.
Related
How to count a number on in Elixir without built-in function such as Enum.count. Here is my code, Thanks so much
defmodule Ans do
#my_favorite_number 0
def sum([]) do
0
end
def sum([head|tail]) do
head + sum(tail)
end
def average([head|tail]) do
total = sum([head|tail])
iterations = Enum.count([head|tail])
output = total / iterations
end
end
You should read about tail-call optimization. The compiler makes use of this optimisation to prevent a new stack frame being created every recursive call, which will happen in your code. Here is an example of how to write the sum/1 function in a tail-recursive way. The main idea is to keep the return in an accumulator variable that is passed to each call, instead of building up the answer in the call stack:
def sum(list), do: sum(0, list)
def sum(acc, []), do: acc
def sum(acc, [head | tail]), do: sum(acc + head, tail)
For count, you can do something similar, but just add 1 instead of the value of the list item:
def count(list), do: count(0, list)
def count(acc, []), do: acc
def count(acc, [_head | tail]), do: count(acc + 1, tail)
While the answer by Adam is perfectly correct, to calculate the average you might do better (in one loop,) using more sophisticated accumulator.
defmodule M do
def avg(list), do: do_avg({0, 0}, list)
defp do_avg({cnt, sum}, []),
do: sum / cnt
defp do_avg({cnt, sum}, [h | t]),
do: do_avg({cnt + 1, sum + h}, t)
end
M.avg [1,2,3,4]
#⇒ 2.5
Here we do accumulate both count and total and calculate an average on the last step when the list is exhausted.
Also, you might return everything, as a tuple {cnt, sum, sum / cnt}, or as a map for better readability.
I have function that splits a list into two halfs.
Here is the function :
let rec split = function
| [] -> ([],[])
| [a] -> ([a],[])
| a::b::cs -> let (M,N) = split cs
(a::M, b::N)
What I don't understand is why this statement works (a::M, b::N). Aren't we calling the recursive function before we execute that statement? So should't that statement never be executed?
Aren't we calling the recursive function before we execute that statement?
Yes.
So should't that statement never be executed?
Only if the recursion were infinite, which it is not. As is, (a::M, b::N) will be evaluated once the recursive call finishes.
As an example, consider the call split [1;2;3]:
split [1;2;3]
= let (M,N) = split [3]
(1::M, 2::N)
= let (M,N) = ([3], [])
(1::M, 2::N)
= (1::[3], 2::[])
= ([1;3], [2])
Nothing infinite going on here.
Why does Elixir have guard clauses for function definition in the module and not in function itself? I find the following to be more intuitive:
def fibonnaci(n) do
when n == 0, do: 0
when n == 1, do: 1
when n > 1 do
fibonnaci(n - 2) + fibonnaci(n - 1)
end
end
I am not sure if this is a question about how to write that code or about why the language was designed like that. Assuming it is the first, you have:
def fibonnaci(0), do: 0
def fibonnaci(1), do: 1
def fibonnaci(n), do: fibonnaci(n - 2) + fibonnaci(n - 1)
In case you want everything in the same function body, you could use a case:
def fibonnaci(n) do
case n do
0 -> ...
If you meant the latter, the answer may become clearer once you get more familiar with the language because the proposed example is not valid syntax (Elixir relies on a simple syntax with very few keywords where everything abides to the same rules).
I recently started with F# and implemented a very basic recursive function that represents the Sieve of Eratosthenes. I came up with the following, working code:
static member internal SieveOfEratosthenesRecursive sequence accumulator =
match sequence with
| [] -> accumulator
| head::tail -> let rest = tail |> List.filter(fun number -> number % head <> 0L)
let newAccumulator = head::accumulator
Prime.SieveOfEratosthenesRecursive rest newAccumulator
This function is not really memory efficient so I tried to eliminate the variables "rest" and "newAccumulator". I came up with the following code
static member internal SieveOfEratosthenesRecursive sequence accumulator =
match sequence with
| [] -> accumulator
| head::tail -> tail |> List.filter(fun number -> number % head <> 0L)
|> Prime.SieveOfEratosthenesRecursive (head::accumulator)
As far as I understand the tutorials I've read Prime.SieveOfEratosthenesRecursive will be called with the filtered tail as first parameter and a list consisting of head::accumulator as second one. However when I try to run the code with the reduced variable usage, the program gets trappen in an infinite loop. Why is this happening and what did I do wrong?
As far as I understand the tutorials I've read Prime.SieveOfEratosthenesRecursive will be called with the filtered tail as first parameter and a list consisting of head::accumulator as second one.
You have this backwards.
In the first version, you're passing rest then newAccumulator; in the second version, you're effectively passing newAccumulator then rest. I.e., you've transposed the arguments.
Prime.SieveOfEratosthenesRecursive (head::accumulator) is a partial function application wherein you're applying (head::accumulator) as the first argument (sequence). This partial function application yields a unary function (expecting accumulator), to which you are passing (via |>) what is called rest in the first version of your code.
Changing SieveOfEratosthenesRecursive's argument order is the easiest solution, but I would consider something like the following idiomatic as well:
static member internal SieveOfEratosthenesRecursive sequence accumulator =
match sequence with
| [] -> accumulator
| head::tail ->
tail
|> List.filter(fun number -> number % head <> 0L)
|> Prime.SieveOfEratosthenesRecursive <| (head::accumulator)
or
static member internal SieveOfEratosthenesRecursive sequence accumulator =
let inline flipzip a b = b, a
match sequence with
| [] -> accumulator
| head::tail ->
tail
|> List.filter(fun number -> number % head <> 0L)
|> flipzip (head::accumulator)
||> Prime.SieveOfEratosthenesRecursive
FWIW, eliminating rest and newAccumulator as named variables here is not going to impact your memory usage in the slightest.
The last call in your second function is equivalent to:
Prime.SieveOfEratosthenesRecursive newAccumulator rest
where you switch positions of two params. Since newAccumulator grows bigger after each recursive call, you will never reach the base case of empty list.
The rule of thumb is putting the most frequently changing parameter at last:
let rec sieve acc xs =
match xs with
| [] -> acc
| x::xs' -> xs' |> List.filter (fun y -> y % x <> 0L)
|> sieve (x::acc)
The above function could be shortened using function keyword:
let rec sieve acc = function
| [] -> acc
| x::xs' -> xs' |> List.filter (fun y -> y % x <> 0L)
|> sieve (x::acc)
Using pipe (|>) operator only makes the function more readable, it doesn't affect memory usage at all.
Can someone give me the difference between these two kinds recursions and example (specifically in OCaml)?
A tail recursive function is a function where the only recursive call is the last one in the function. A non-tail recursive function is a function where that is not the case.
A backward recursion is a recursion where in each recursive call the value of the parameter is less than in the previous step. A forward recursion is a recursion where it grows bigger with each step.
Those are two orthogonal concepts, i.e. a forward recursion may or may not be tail-recursive and the same applies to backward recursions.
For example the factorial function is often written like this in imperative languages:
fac = 1
for i from 1 to n:
fac := fac * i
The common recursive version of factorial counts backwards (i.e. it calls itself with n-1 as the parameter), however if you'd directly translate the above imperative solution, you'd come up with a recursive version that counts upwards. It would look something like this:
let fac n =
let rec loop i =
if i >= n
then i
else i * loop (i+1)
in
loop 1
This is a forward recursion and as you can see it is slightly more cumbersome than the backward recursive variant as it requires a helper function. Now this is not tail recursive as the last call in loop is the multiplication, not the recursion. So to make it tail-recursive, you'd do something like this:
let fac n =
let rec loop acc i =
if i >= n
then acc
else loop (i*acc) (i+1)
in
loop 1 1
Now this is both a forward recursion and a tail recursion because the recursive call is a) a tail-call and b) calls itself with a greater value (i+1).
Here's an example of a tail recursive factorial function:
let fac n =
let rec f n a =
match n with
0 -> a
| _ -> f (n-1) (n*a)
in
f n 1
Here is it's non-tail recursive counterpart:
let rec non_tail_fac n =
match n with
0 -> 1
| _ -> (non_tail_fac n-1) * n
The tail recursive function uses an accumulator, a, to store the value of the result of the previous call. This allows OCaml to perform tail call optimization which results in the the stack not overflowing. Typically a tail recursive function will make use of an accumulator value to allow tail call optimization to occur.
For example, a recursive function build_word which takes a char list and combine them to a string i.e.['f'; 'o'; 'o'] to string "foo". The induction process can be visualized this way:
build_word ['f'; 'o'; 'o']
"f" ^ (build_word ['o'; 'o'])
"f" ^ ("o" ^ (build_word ['o']) // base case! return "o" and fold back
"f" ^ ("o" ^ ("o"))
"f" ^ ("oo")
"foo"
That was a normal recursion. Note that each pair of parentheses stands for a new stack frame or recursive call. The solution to this problem (i.e. "f", "fo", or "foo") cannot be derived before the end of the recursion (where the base case is met). Only then does the last frame return the last result back to the previous one before "popping", and vice versa.
In theory, each call creates a new stack frame (or scope, if you will) to hold the "place" for the fragmented solution to be returned and collected toward the beginning. This can leads to stackoverflow (this link is a recursion btw).
A tail call version would look something like this:
build_word ['f'; 'o'; 'o'] ""
build_word ['o'; 'o'], "f"
build_word ['o'] ("f" ^ "o")
build_word [] ("f" ^ "o" ^ "o")
"foo"
Here, the accumulated result (often stored in a variable known as accumulator) is being passed forward. With optimization, tail call wouldn't have to create a new stack frame because it does not have to maintain the previous ones. The solution is being solved "forward" rather than "backward".
Here are the build_word functions in two versions:
non-tail
let build_word chars =
match chars with
| [] -> None
| [c] -> Some Char.to_string c
| hd :: tl -> build_word tl
tail
let build_word ?(acc = "") chars =
match chars with
| [] -> None
| [c] -> Some Char.to_string c
| hd::tl -> build_word ~acc:(acc ^ Char.to_string hd) tl
The forward recursion is well-explained in the accepted answer by #sepp2k.