Sök…


Syntax

  • funktion (lista | iolist | tuple) -> funktion (svans).

Anmärkningar

Varför rekursiva funktioner?

Erlang är ett funktionellt programmeringsspråk och har ingen form för slingstruktur. Allt i funktionell programmering är baserat på data, typ och funktioner. Om du vill ha en slinga måste du skapa en funktion som kallar sig själv.

Traditionell while eller for loop i imperativt och objektorienterat språk kan representeras på det sättet i Erlang:

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

En bra metod för att förstå detta koncept är att utöka alla funktionssamtal. Vi ser det på andra exempel.

Lista

Här enklaste rekursiv funktion över listan typ. Denna funktion navigerar bara till en lista från början till slut och gör ingenting mer.

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

Du kan kalla det så här:

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

Här utvidgar den rekursiva funktionen:

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

Rekursiv slinga med IO-åtgärder

Tidigare kod gör ingenting och är ganska värdelös. Så vi kommer nu att skapa en rekursiv funktion som utför vissa åtgärder. Den här koden liknar lists:foreach/2 .

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

Du kan ringa det så här:

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

Här utvidgar den rekursiva funktionen:

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

Du kan jämföra med lists:foreach/2 utgång:

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

Rekursiv loop över lista som returnerar modifierad lista

Ett annat användbart exempel, liknande lists:map/2 . Denna funktion tar en lista och en anonym funktion. Varje gång ett värde i listan matchas tillämpar vi funktionen på den.

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

Du kan kalla det så här:

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

Här utvidgar den rekursiva funktionen:

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

Denna funktion kallas också "svansrekursiv funktion", eftersom vi använder en variabel som en ackumulator för att skicka modifierad data över flera exekveringssituationer.

Iolist och Bitstring

Som lista är den enklaste funktionen över iolist och bitsträng :

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

Du kan kalla det så här:

loop(<<"abc">>).

Här utvidgar den rekursiva funktionen:

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

Rekursiv funktion över variabel binär storlek

Denna kod tar bitsträng och definierar dynamisk binärstorlek dynamiskt. Så om, om vi ställer in en storlek på 4 , var 4 bit, kommer data att matchas. Denna slinga gör inget intressant, det är bara vår pelare.

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.

Du kan kalla det så här:

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

Här utvidgar den rekursiva funktionen:

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.

Vår bitsträng är uppdelad över sju mönster. Varför? Eftersom Erlang som standard använder binär storlek på 8 bitar, om vi delar upp det i två, har vi 4 bitar. Vår sträng är 8*3=24 bitar. 24/4=6 mönster. Det sista mönstret är <<>> . loop/2 funktion kallas 7 gånger.

rekursiv funktion över variabel binär storlek med åtgärder

Nu kan vi göra mer intressant. Denna funktion tar ytterligare ett argument, en anonym funktion. Varje gång vi matchar ett mönster kommer denna att överföras till det.

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

Du kan kalla det så här:

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

Här utvidgar den rekursiva funktionen:

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.

Rekursiv funktion över bitsträng som returnerar modifierad bitsträng

Den här liknar lists:map/2 men för bitsträng och 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.

Denna kod verkar mer komplex. Två funktioner lades till: loop/2 och loop/3 . Dessa två funktioner är enkla gränssnitt till loop/4 .

Du kan köra det så här:

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

Karta

Karta i Erlang motsvarar hash i Perl eller ordböcker i Python, det är en nyckel- / värde-butik. Om du vill lista alla värden som lagras i kan du lista alla tangenter och returnera nyckel / värdepar. Den här första slingan ger dig en idé:

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

Du kan köra det så:

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

Hantera staten

Rekursiv funktion använder sina tillstånd för att slinga. När du skapar en ny process kommer denna process helt enkelt att vara en slinga med något definierat tillstånd.

Anonym funktion

Här två exempel på rekursiva anonyma funktioner baserade på tidigare exempel. För det första enkel oändlig slinga:

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

För det andra, anonym funktion gör loop-över-listan:

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

Dessa två funktioner kan skrivas om som:

InfiniteLoop = fun loop/0.

I detta fall är loop/0 en hänvisning till loop/0 från anmärkningar. För det andra, med lite mer komplex:

LoopOverLlist = fun loop/2.

Här är loop/2 en referens till loop/2 från listexempel. Dessa två notationer är syntaktiskt socker.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow