Поиск…


Синтаксис

  • function (list | iolist | tuple) -> function (tail).

замечания

Почему рекурсивные функции?

Erlang - это функциональный язык программирования и не имеет никакой структуры циклов. Все функциональное программирование основано на данных, типе и функциях. Если вам нужен цикл, вам нужно создать функцию, которая сама вызывает вызов.

Традиционный while или for loop в императивном и объектно-ориентированном языке может быть представлен таким же образом в Erlang:

loop() ->
  % do something here
  loop().

Хорошим методом понимания этой концепции является расширение всех вызовов функций. Мы увидим это на других примерах.

Список

Здесь простейшая рекурсивная функция над типом списка . Эта функция только переходит в список от начала до конца и больше ничего не делает.

-spec loop(list()) -> ok.
loop([]) ->
  ok;
loop([H|T]) ->
  loop(T).

Вы можете назвать это следующим образом:

loop([1,2,3]). % will return ok.

Здесь разложение рекурсивной функции:

loop([1|2,3]) ->
  loop([2|3]) ->
    loop([3|]) ->
      loop([]) ->
        ok.

Рекурсивный цикл с действиями IO

Предыдущий код ничего не делает и довольно бесполезен. Итак, мы создадим теперь рекурсивную функцию, выполняющую некоторые действия. Этот код похож на lists:foreach/2 .

-spec loop(list(), fun()) -> ok.
loop([], _) ->
  ok;
loop([H|T], Fun) 
  when is_function(Fun) ->
    Fun(H),
    loop(T, Fun).

Вы можете вызвать его так:

Fun = fun(X) -> io:format("~p", [X]) end.
loop([1,2,3]).

Здесь разложение рекурсивной функции:

loop([1|2,3], Fun(1)) ->
  loop([2|3], Fun(2)) ->
    loop([3|], Fun(3)) ->
      loop([], _) ->
        ok.

Вы можете сравнить со lists:foreach/2 output:

lists:foreach(Fun, [1,2,3]).

Рекурсивный цикл над списком, возвращающим измененный список

Другой полезный пример, аналогичный lists:map/2 . Эта функция будет принимать один список и одну анонимную функцию. Каждый раз, когда значение в списке сопоставляется, мы применяем к нему функцию.

-spec loop(A :: list(), fun()) -> list().
loop(List, Fun) 
  when is_list(List), is_function(Fun) ->
    loop(List, Fun, []).

-spec loop(list(), fun(), list()) -> list() | {error, list()}.
loop([], _, Buffer) 
  when is_list(Buffer) ->
    lists:reverse(Buffer);
loop([H|T], Fun, Buffer) 
  when is_function(Fun), is_list(Buffer) ->
    BufferReturn = [Fun(H)] ++ Buffer,
    loop(T, Fun, BufferReturn).

Вы можете назвать это следующим образом:

Fun(X) -> X+1 end.
loop([1,2,3], Fun).

Здесь разложение рекурсивной функции:

loop([1|2,3], Fun(1), [2]) ->
  loop([2|3], Fun(2), [3,2]) ->
    loop([3|], Fun(3), [4,3,2]) ->
      loop([], _, [4,3,2]) ->
        list:reverse([4,3,2]) ->
          [2,3,4].

Эта функция также называется «хвостовой рекурсивной функцией», потому что мы используем переменную, подобную аккумулятору, для передачи измененных данных по нескольким контекстам выполнения.

Iolist и битстрим

Как и список, простейшая функция над iolist и bitstring :

-spec loop(iolist()) -> ok | {ok, iolist} .
loop(<<>>) ->
  ok;
loop(<<Head, Tail/bitstring>>) ->
  loop(Tail);
loop(<<Rest/bitstring>>) ->
  {ok, Rest}

Вы можете назвать это следующим образом:

loop(<<"abc">>).

Здесь разложение рекурсивной функции:

loop(<<"a"/bitstring, "bc"/bitstring>>) ->
  loop(<<"b"/bitstring, "c"/bitstring>>) ->
    loop(<<"c"/bitstring>>) ->
      loop(<<>>) ->
        ok.

Рекурсивная функция по переменному двоичному размеру

Этот код принимает bitstring и динамически определяет двоичный размер. Поэтому, если, если мы установим размер 4 , каждые 4 бита, данные будут сопоставлены. Этот цикл не делает ничего интересного, его просто наш столп.

loop(Bitstring, Size) 
  when is_bitstring(Bitstring), is_integer(Size) ->
    case Bitstring of
      <<>> -> 
        ok;
      <<Head:Size/bitstring,Tail/bitstring>> ->
        loop(Tail, Size);
      <<Rest/bitstring>> ->
        {ok, Rest}
    end.

Вы можете назвать это следующим образом:

loop(<<"abc">>, 4).

Здесь разложение рекурсивной функции:

loop(<<6:4/bitstring, 22, 38, 3:4>>, 4) ->
  loop(<<1:4/bitstring, "bc">>, 4) ->
    loop(<<6:4/bitstring, 38,3:4>>, 4) ->
      loop(<<2:4/bitstring, "c">>, 4) ->
        loop(<<6:4/bitstring, 3:4>>, 4) ->
          loop(<<3:4/bitstring>>, 4) ->
            loop(<<>>, 4) ->
              ok.

Наша битстрима разделена на 7 шаблонов. Зачем? Поскольку по умолчанию Erlang использует двоичный размер 8 бит, если мы разделим его на две части, у нас есть 4 бита. Наша строка 8*3=24 бит. 24/4=6 рисунков. Последний шаблон - <<>> . Функция loop/2 называется 7 раз.

рекурсивная функция по переменному двоичному размеру с действиями

Теперь мы можем сделать более интересную вещь. Эта функция принимает еще один аргумент - анонимную функцию. Каждый раз, когда мы сопоставляем шаблон, он будет передан ему.

-spec loop(iolist(), integer(), function()) -> ok.
loop(Bitstring, Size, Fun) ->
  when is_bitstring(Bitstring), is_integer(Size), is_function(Fun) ->
        case Bitstring of
      <<>> -> 
        ok;
      <<Head:Size/bitstring,Tail/bitstring>> ->
        Fun(Head),
        loop(Tail, Size, Fun);
      <<Rest/bitstring>> ->
        Fun(Rest),
        {ok, Rest}
    end.

Вы можете назвать это следующим образом:

Fun = fun(X) -> io:format("~p~n", [X]) end.
loop(<<"abc">>, 4, Fun).

Здесь разложение рекурсивной функции:

loop(<<6:4/bitstring, 22, 38, 3:4>>, 4, Fun(<<6:4>>) ->
  loop(<<1:4/bitstring, "bc">>, 4, Fun(<<1:4>>)) ->
    loop(<<6:4/bitstring, 38,3:4>>, 4, Fun(<<6:4>>)) ->
      loop(<<2:4/bitstring, "c">>, 4, Fun(<<2:4>>)) ->
        loop(<<6:4/bitstring, 3:4>>, 4, Fun(<<6:4>>) ->
          loop(<<3:4/bitstring>>, 4, Fun(<<3:4>>) ->
            loop(<<>>, 4) ->
              ok.

Рекурсивная функция над битовой строкой, возвращающая измененную bitstring

Это похоже на lists:map/2 но для bitstring и iolist.

% public function (interface).
-spec loop(iolist(), fun()) -> iolist() | {iolist(), iolist()}.
loop(Bitstring, Fun) ->
  loop(Bitstring, 8, Fun).

% public function (interface).
-spec loop(iolist(), integer(), fun()) -> iolist() | {iolist(), iolist()}.
loop(Bitstring, Size, Fun) ->
  loop(Bitstring, Size, Fun, <<>>)

% private function.
-spec loop(iolist(), integer(), fun(), iolist()) -> iolist() | {iolist(), iolist()}.
loop(<<>>, _, _, Buffer) ->
  Buffer;
loop(Bitstring, Size, Fun, Buffer) ->
  when is_bitstring(Bitstring), is_integer(Size), is_function(Fun) ->
    case Bitstring of
      <<>> -> 
        Buffer;
      <<Head:Size/bitstring,Tail/bitstring>> ->
        Data = Fun(Head),
        BufferReturn = <<Buffer/bitstring, Data/bitstring>>,
        loop(Tail, Size, Fun, BufferReturn);
      <<Rest/bitstring>> ->
        {Buffer, Rest}
    end.

Этот код кажется более сложным. Добавлены две функции: loop/2 и loop/3 . Эти две функции - простой интерфейс для loop/4 .

Вы можете выполнить его следующим образом:

Fun = fun(<<X>>) -> << (X+1) >> end.
loop(<<"abc">>, Fun).
% will return <<"bcd">>

Fun = fun(<<X:4>>) -> << (X+1) >> end.
loop(<<"abc">>, 4, Fun).
% will return <<7,2,7,3,7,4>>

loop(<<"abc">>, 4, Fun, <<>>).
% will return <<7,2,7,3,7,4>>

карта

Карта в Erlang эквивалентна хэшам в Perl или словарям в Python, его хранилище ключей / значений. Чтобы перечислить каждое сохраненное значение, вы можете перечислить каждый ключ и вернуть пару ключ / значение. Этот первый цикл дает вам представление:

loop(Map) when is_map(Map) -> 
  Keys = maps:keys(Map),
  loop(Map, Keys).

loop(_ , []) ->
  ok;
loop(Map, [Head|Tail]) ->
  Value = maps:get(Head, Map),
  io:format("~p: ~p~n", [Head, Value]),
  loop(Map, Tail).

Вы можете выполнить его так:

Map = #{1 => "one", 2 => "two", 3 => "three"}.
loop(Map).
% will return:
% 1: "one"
% 2: "two"
% 3: "three"

Управляющее государство

Рекурсивная функция использует свои состояния в цикле. Когда вы создаете новый процесс, этот процесс будет просто циклом с некоторым определенным состоянием.

Анонимная функция

Вот 2 примера рекурсивных анонимных функций, основанных на предыдущем примере. Во-первых, простой бесконечный цикл:

InfiniteLoop = fun 
  R() -> 
    R() end.

Во-вторых, анонимная функция делает цикл над списком:

LoopOverList = fun 
  R([]) -> ok;
  R([H|T]) ->
    R(T) end.

Эти две функции можно переписать следующим образом:

InfiniteLoop = fun loop/0.

В этом случае loop/0 является ссылкой на loop/0 из замечаний. Во-вторых, с немного более сложными:

LoopOverLlist = fun loop/2.

Здесь loop/2 является ссылкой на loop/2 из примера списка. Эти две записи являются синтаксическим сахаром.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow