Erlang basics · Lesson 08 (open contents)
Let a function keep going
Stop at an empty list and use a head-and-tail pattern for the next item.
Copy the whole block into the erl shell
Read the [] clause first, then the [Head | Tail] clause. Do not try to follow every call at once.
%% Clear variables left in the shell by earlier lessons
f().
%% Double stores the function; Loop is only for recursion inside it
Double =
fun Loop([]) ->
[];
Loop([Head | Tail]) ->
[Head * 2 | Loop(Tail)]
end.
%% Call the function with its outside name
Double([1, 2, 3]).Double([1, 2, 3])returns[2, 4, 6].- The list becomes one item shorter with each call.
- When the input reaches
Loop([]), it returns[]and the recursion stops.
Read from the first line down
- For the input
[1, 2, 3],Headis 1 andTailis[2, 3]. - This layer prepares
1 * 2. The remaining work becomesLoop([2, 3]). Loopis visible only inside thisfun. Code outside calls the whole function through the variableDouble.- The semicolon says another
funclause follows. The last clause goes straight toend.. - At last, the empty-list clause returns
[]. The results from the earlier layers are then joined back together.
Symbols are not secret signs
fun Loop([]) ->Create an anonymous function with the internal name Loop. Its first clause accepts an empty list.
Loop([Head | Tail]) ->The second clause splits the current item from the remaining items.
[Head * 2 | Loop(Tail)]Put in the current result, then let Loop handle the shorter Tail.
Match each name to its meaning
recursion
A function calls itself inside its own definition.
function clause
One entry point for a function. Each clause accepts a different shape of argument.
stopping condition
A case that does not call the function again. This example returns when it reaches an empty list.
Why these forms are useful
Erlang often processes a list with recursion. A function handles the first item, then gives the remaining list to itself.
Recursion needs a stopping condition. For a list, the empty list [] is a natural finish line.
One function can have several clauses. Erlang searches from top to bottom for a clause with the right shape. Here, the first clause receives an empty list and the second receives [Head | Tail].
Close the answer and try
Complete AddOne so that it adds 1 to every number in a list.
%% Clear old variables so you can practice again
f().
%% Keep the stopping condition as it is
AddOne =
fun Loop([]) ->
[];
Loop([Head | Tail]) ->
[____ | Loop(Tail)]
end.
%% Call the function and check its result
AddOne([2, 3, 4]).Target result: AddOne([2, 3, 4]) should return [3, 4, 5].
Stuck? Read one hint
The new value for the current item is Head + 1.
After you run it, see one answer
%% f() belongs in the shell, not in an .erl file
f().
%% AddOne is the outside name; Loop is the inside recursion name
AddOne =
fun Loop([]) ->
[];
Loop([Head | Tail]) ->
[Head + 1 | Loop(Tail)]
end.
%% This should return [3,4,5]
AddOne([2, 3, 4]).Think about it: Where does this recursion stop?
When the input becomes an empty list, Loop([]) -> [] returns directly and does not call itself again.
Remember these three lines
- 1
Recursion turns a large problem into a smaller problem of the same kind.
- 2
Without the
[]clause, this example reaches an empty list and raisesfunction_clause. Recursion only runs forever when the problem does not get smaller. - 3
Find the stopping condition before reading the recursive clause.