Erlang递归与循环 #
一、递归概述 #
Erlang没有传统循环语句(for、while),所有循环都通过递归实现。
1.1 为什么用递归 #
- 函数式编程范式
- 不可变变量
- 易于推理和验证
- 支持尾递归优化
1.2 递归结构 #
erlang
-module(recursion_structure).
-export([demo/0]).
demo() ->
ok.
recursive_function(BaseCase) ->
BaseResult;
recursive_function(RecursiveCase) ->
Processed = process(RecursiveCase),
recursive_function(Processed).
process(X) -> X.
二、基本递归 #
2.1 阶乘 #
erlang
-module(factorial).
-export([factorial/1]).
factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).
2.2 斐波那契 #
erlang
-module(fibonacci).
-export([fib/1]).
fib(0) -> 0;
fib(1) -> 1;
fib(N) when N > 1 -> fib(N - 1) + fib(N - 2).
2.3 列表长度 #
erlang
-module(list_length).
-export([length/1]).
length([]) -> 0;
length([_ | T]) -> 1 + length(T).
2.4 列表求和 #
erlang
-module(list_sum).
-export([sum/1]).
sum([]) -> 0;
sum([H | T]) -> H + sum(T).
三、尾递归 #
3.1 什么是尾递归 #
尾递归是递归调用是函数最后执行的操作。
erlang
-module(tail_recursive).
-export([factorial/1, factorial/2]).
factorial(N) -> factorial(N, 1).
factorial(0, Acc) -> Acc;
factorial(N, Acc) when N > 0 -> factorial(N - 1, N * Acc).
3.2 非尾递归 vs 尾递归 #
erlang
-module(tail_comparison).
-export([sum_bad/1, sum_good/1]).
sum_bad([]) -> 0;
sum_bad([H | T]) -> H + sum_bad(T).
sum_good(List) -> sum_good(List, 0).
sum_good([], Acc) -> Acc;
sum_good([H | T], Acc) -> sum_good(T, H + Acc).
3.3 尾递归优化 #
尾递归会被编译器优化为循环,不会增加栈深度:
erlang
-module(tail_optimization).
-export([count_down/1]).
count_down(0) -> done;
count_down(N) when N > 0 ->
io:format("~p~n", [N]),
count_down(N - 1).
四、累加器模式 #
4.1 基本累加器 #
erlang
-module(accumulator).
-export([reverse/1]).
reverse(List) -> reverse(List, []).
reverse([], Acc) -> Acc;
reverse([H | T], Acc) -> reverse(T, [H | Acc]).
4.2 多累加器 #
erlang
-module(multi_accumulator).
-export([split/2]).
split(N, List) -> split(N, List, [], []).
split(0, List, Left, Right) -> {lists:reverse(Left), List};
split(_, [], Left, Right) -> {lists:reverse(Left), []};
split(N, [H | T], Left, Right) -> split(N - 1, T, [H | Left], Right).
4.3 复杂累加器 #
erlang
-module(complex_accumulator).
-export([group_by_length/1]).
group_by_length(List) ->
Result = group_by_length(List, #{}),
maps:map(fun(_, V) -> lists:reverse(V) end, Result).
group_by_length([], Acc) -> Acc;
group_by_length([H | T], Acc) ->
Len = length(H),
Current = maps:get(Len, Acc, []),
group_by_length(T, Acc#{Len => [H | Current]}).
五、递归模式 #
5.1 列表遍历 #
erlang
-module(list_traverse).
-export([print_all/1, find/2]).
print_all([]) -> ok;
print_all([H | T]) ->
io:format("~p~n", [H]),
print_all(T).
find(_, []) -> not_found;
find(Value, [Value | _]) -> {found, Value};
find(Value, [_ | T]) -> find(Value, T).
5.2 列表转换 #
erlang
-module(list_transform).
-export([map/2, filter/2]).
map(_, []) -> [];
map(Fun, [H | T]) -> [Fun(H) | map(Fun, T)].
filter(_, []) -> [];
filter(Pred, [H | T]) ->
case Pred(H) of
true -> [H | filter(Pred, T)];
false -> filter(Pred, T)
end.
5.3 列表折叠 #
erlang
-module(list_fold).
-export([foldl/3, foldr/3]).
foldl(_, Acc, []) -> Acc;
foldl(Fun, Acc, [H | T]) -> foldl(Fun, Fun(H, Acc), T).
foldr(_, Acc, []) -> Acc;
foldr(Fun, Acc, [H | T]) -> Fun(H, foldr(Fun, Acc, T)).
5.4 树遍历 #
erlang
-module(tree_traverse).
-export([sum/1, map/2]).
sum({node, Value, Left, Right}) ->
Value + sum(Left) + sum(Right);
sum(empty) -> 0.
map(Fun, {node, Value, Left, Right}) ->
{node, Fun(Value), map(Fun, Left), map(Fun, Right)};
map(_Fun, empty) -> empty.
六、相互递归 #
6.1 基本相互递归 #
erlang
-module(mutual_recursion).
-export([is_even/1, is_odd/1]).
is_even(0) -> true;
is_even(N) when N > 0 -> is_odd(N - 1).
is_odd(0) -> false;
is_odd(N) when N > 0 -> is_even(N - 1).
6.2 状态机 #
erlang
-module(state_machine).
-export([start/0]).
start() -> idle().
idle() ->
receive
connect -> connected();
stop -> stopped
end.
connected() ->
receive
disconnect -> idle();
{send, Data} ->
io:format("Sending: ~p~n", [Data]),
connected();
stop -> stopped
end.
七、递归与性能 #
7.1 栈溢出 #
erlang
-module(stack_overflow).
-export([bad/1, good/1]).
bad(0) -> done;
bad(N) -> bad(N - 1), ok.
good(0) -> done;
good(N) -> good(N - 1).
7.2 内存使用 #
erlang
-module(memory_usage).
-export([bad_sum/1, good_sum/1]).
bad_sum([]) -> 0;
bad_sum([H | T]) -> H + bad_sum(T).
good_sum(List) -> good_sum(List, 0).
good_sum([], Acc) -> Acc;
good_sum([H | T], Acc) -> good_sum(T, H + Acc).
7.3 优化技巧 #
erlang
-module(optimization).
-export([process_list/1]).
process_list(List) ->
process_list(List, []).
process_list([], Acc) -> lists:reverse(Acc);
process_list([H | T], Acc) ->
NewH = process_item(H),
process_list(T, [NewH | Acc]).
process_item(X) -> X * 2.
八、实际应用 #
8.1 解析器 #
erlang
-module(parser).
-export([parse/1]).
parse(String) -> parse(String, []).
parse([], Acc) -> {ok, lists:reverse(Acc)};
parse([${ | Rest], Acc) -> parse(Rest, [open_brace | Acc]);
parse([$} | Rest], Acc) -> parse(Rest, [close_brace | Acc]);
parse([$[ | Rest], Acc) -> parse(Rest, [open_bracket | Acc]);
parse([$] | Rest], Acc) -> parse(Rest, [close_bracket | Acc]);
parse([$, | Rest], Acc) -> parse(Rest, [comma | Acc]);
parse([$: | Rest], Acc) -> parse(Rest, [colon | Acc]);
parse([C | Rest], Acc) when C >= $0, C =< $9 ->
{Number, NewRest} = parse_number(Rest, [C]),
parse(NewRest, [{number, list_to_integer(Number)} | Acc]);
parse([C | Rest], Acc) when C >= $a, C =< $z ->
{Word, NewRest} = parse_word(Rest, [C]),
parse(NewRest, [{word, lists:reverse(Word)} | Acc]);
parse([$\s | Rest], Acc) -> parse(Rest, Acc);
parse([$\n | Rest], Acc) -> parse(Rest, Acc);
parse([$\t | Rest], Acc) -> parse(Rest, Acc).
parse_number([], Acc) -> {lists:reverse(Acc), []};
parse_number([C | Rest], Acc) when C >= $0, C =< $9 ->
parse_number(Rest, [C | Acc]);
parse_number(Rest, Acc) -> {lists:reverse(Acc), Rest}.
parse_word([], Acc) -> {Acc, []};
parse_word([C | Rest], Acc) when C >= $a, C =< $z ->
parse_word(Rest, [C | Acc]);
parse_word(Rest, Acc) -> {Acc, Rest}.
8.2 文件遍历 #
erlang
-module(file_traverse).
-export([list_files/1]).
list_files(Dir) -> list_files(Dir, []).
list_files([], Acc) -> lists:reverse(Acc);
list_files([Dir | Rest], Acc) ->
case file:list_dir(Dir) of
{ok, Files} ->
{Dirs, Files2} = lists:partition(
fun(F) -> is_dir(filename:join(Dir, F)) end,
Files
),
FullDirs = [filename:join(Dir, D) || D <- Dirs],
FullFiles = [filename:join(Dir, F) || F <- Files2],
list_files(Rest ++ FullDirs, FullFiles ++ Acc);
{error, _} ->
list_files(Rest, Acc)
end.
is_dir(Path) ->
case file:read_file_info(Path) of
{ok, #file_info{type = directory}} -> true;
_ -> false
end.
8.3 消息处理循环 #
erlang
-module(message_loop).
-export([start/0, loop/1]).
start() ->
spawn(fun() -> loop(#{}) end).
loop(State) ->
receive
{get, Key, From} ->
Value = maps:get(Key, State, undefined),
From ! {value, Value},
loop(State);
{set, Key, Value} ->
NewState = State#{Key => Value},
loop(NewState);
{delete, Key} ->
NewState = maps:remove(Key, State),
loop(NewState);
stop ->
ok;
_ ->
loop(State)
end.
九、递归最佳实践 #
9.1 总是使用尾递归 #
erlang
-module(best_tail).
-export([good/1, bad/1]).
bad([]) -> 0;
bad([H | T]) -> H + bad(T).
good(List) -> good(List, 0).
good([], Acc) -> Acc;
good([H | T], Acc) -> good(T, H + Acc).
9.2 清晰的基准条件 #
erlang
-module(clear_base).
-export([process/1]).
process([]) -> done;
process([H | T]) ->
handle(H),
process(T).
handle(_) -> ok.
9.3 使用帮助函数 #
erlang
-module(helper_func).
-export([reverse/1]).
reverse(List) -> reverse(List, []).
reverse([], Acc) -> Acc;
reverse([H | T], Acc) -> reverse(T, [H | Acc]).
十、总结 #
本章学习了:
- 递归的基本概念
- 尾递归优化
- 累加器模式
- 递归模式
- 相互递归
- 性能考虑
- 实际应用
- 最佳实践
准备好学习异常处理了吗?让我们进入下一章。
最后更新:2026-03-27