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