Szukaj…


Składnia

  • funkcja (lista | iolist | krotka) -> funkcja (ogon).

Uwagi

Dlaczego funkcje rekurencyjne?

Erlang jest funkcjonalnym językiem programowania i nie ma struktury pętli. Wszystko w programowaniu funkcjonalnym opiera się na danych, typie i funkcjach. Jeśli chcesz uzyskać pętlę, musisz utworzyć funkcję, która się nazywa.

Tradycyjna pętla while lub for w języku rozkazującym i obiektowym może być reprezentowana w Erlang w następujący sposób:

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

Dobrym sposobem na zrozumienie tej koncepcji jest rozszerzenie wszystkich wywołań funkcji. Zobaczymy to na innych przykładach.

Lista

Tutaj najprostsza funkcja rekurencyjna nad typem listy . Ta funkcja nawiguje tylko po liście od początku do końca i nic więcej nie robi.

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

Możesz to tak nazwać:

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

Tutaj rozwinięcie funkcji rekurencyjnej:

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

Pętla rekurencyjna z akcjami IO

Poprzedni kod nic nie robi i jest całkiem bezużyteczny. Dlatego stworzymy teraz funkcję rekurencyjną, która wykona niektóre akcje. Ten kod jest podobny do lists:foreach/2 .

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

Możesz to nazwać w następujący sposób:

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

Tutaj rozwinięcie funkcji rekurencyjnej:

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

Możesz porównać z lists:foreach/2 output:

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

Pętla rekurencyjna nad listą zwraca zmodyfikowaną listę

Kolejny przydatny przykład, podobny do lists:map/2 . Ta funkcja przyjmuje jedną listę i jedną anonimową funkcję. Za każdym razem, gdy wartość na liście jest dopasowywana, stosujemy na niej funkcję.

-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).

Możesz to tak nazwać:

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

Tutaj rozwinięcie funkcji rekurencyjnej:

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].

Ta funkcja jest również nazywana „funkcją rekurencyjną ogona”, ponieważ używamy zmiennej, takiej jak akumulator, do przekazywania zmodyfikowanych danych w kontekście wielokrotnego wykonywania.

Iolist i Bitstring

Podobnie jak lista, najprostszą funkcją nad iolist i ciągiem bitów jest:

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

Możesz to tak nazwać:

loop(<<"abc">>).

Tutaj rozwinięcie funkcji rekurencyjnej:

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

Funkcja rekurencyjna na zmiennym rozmiarze binarnym

Ten kod przyjmuje łańcuch bitów i dynamicznie definiuje jego rozmiar binarny. Jeśli więc ustawimy rozmiar 4 , co 4 bity, dane zostaną dopasowane. Ta pętla nie robi nic interesującego, to tylko nasz filar.

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.

Możesz to tak nazwać:

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

Tutaj rozwinięcie funkcji rekurencyjnej:

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.

Nasz ciąg bitów jest podzielony na 7 wzorów. Dlaczego? Ponieważ domyślnie Erlang używa binarnego rozmiaru 8 bitów, jeśli podzielimy go na dwa, otrzymamy 4 bity. Nasz ciąg ma 8*3=24 bity. 24/4=6 wzorów. Ostatni wzór to <<>> . Funkcja loop/2 jest wywoływana 7 razy.

funkcja rekurencyjna nad zmiennym rozmiarem binarnym z akcjami

Teraz możemy zrobić coś ciekawszego. Ta funkcja wymaga jeszcze jednego argumentu, funkcji anonimowej. Za każdym razem, gdy dopasujemy wzór, ten zostanie do niego przekazany.

-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.

Możesz to tak nazwać:

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

Tutaj rozwinięcie funkcji rekurencyjnej:

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.

Funkcja rekurencyjna nad ciągiem bitów zwraca zmodyfikowany ciąg bitów

Ten jest podobny do lists:map/2 ale do bitstringów i 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.

Ten kod wydaje się bardziej złożony. Dodano dwie funkcje: loop/2 i loop/3 . Te dwie funkcje to prosty interfejs do loop/4 .

Możesz wykonać to w następujący sposób:

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>>

Mapa

Mapa w Erlang jest odpowiednikiem skrótów w Perlu lub słowników w Pythonie, jest to magazyn kluczy / wartości. Aby wyświetlić listę wszystkich przechowywanych wartości, możesz wymienić każdy klucz i zwrócić parę klucz / wartość. Ta pierwsza pętla daje pomysł:

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).

Możesz to wykonać w następujący sposób:

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

Państwo zarządzające

Funkcje rekurencyjne używają swoich stanów do zapętlania. Kiedy spawnujesz nowy proces, proces ten będzie po prostu pętlą o określonym stanie.

Funkcja anonimowa

Tutaj 2 przykłady rekurencyjnych funkcji anonimowych na podstawie poprzedniego przykładu. Po pierwsze, prosta nieskończona pętla:

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

Po drugie, anonimowa funkcja robi pętlę nad listą:

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

Te dwie funkcje można przepisać jako:

InfiniteLoop = fun loop/0.

W tym przypadku loop/0 jest odwołaniem do loop/0 z uwag. Po drugie, z nieco bardziej złożonymi:

LoopOverLlist = fun loop/2.

Tutaj loop/2 jest odniesieniem do loop/2 z przykładu listy. Te dwie notacje to cukier składniowy.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow