Erlang Language
Lus en recursie
Zoeken…
Syntaxis
- functie (lijst | iolist | tuple) -> functie (staart).
Opmerkingen
Waarom recursieve functies?
Erlang is een functionele programmeertaal en kent geen enkele lusstructuur. Alles in functioneel programmeren is gebaseerd op gegevens, type en functies. Als u een lus wilt, moet u een functie maken die zichzelf aanroept.
Traditioneel while
of for
loop in gebiedende en objectgeoriënteerde taal kan op dezelfde manier worden weergegeven in Erlang:
loop() ->
% do something here
loop().
Een goede methode om dit concept te begrijpen, is om alle functieaanroepen uit te breiden. We zullen dat op andere voorbeelden zien.
Lijst
Hier de eenvoudigste recursieve functie boven het lijsttype . Deze functie navigeert alleen van het begin tot het einde in een lijst en doet niets meer.
-spec loop(list()) -> ok.
loop([]) ->
ok;
loop([H|T]) ->
loop(T).
Je kunt het zo noemen:
loop([1,2,3]). % will return ok.
Hier de recursieve functie-uitbreiding:
loop([1|2,3]) ->
loop([2|3]) ->
loop([3|]) ->
loop([]) ->
ok.
Recursieve lus met IO-acties
Vorige code doet niets en is behoorlijk nutteloos. Dus zullen we nu een recursieve functie maken die sommige acties uitvoert. Deze code is vergelijkbaar met lists:foreach/2
.
-spec loop(list(), fun()) -> ok.
loop([], _) ->
ok;
loop([H|T], Fun)
when is_function(Fun) ->
Fun(H),
loop(T, Fun).
Je kunt het zo noemen:
Fun = fun(X) -> io:format("~p", [X]) end.
loop([1,2,3]).
Hier de recursieve functie-uitbreiding:
loop([1|2,3], Fun(1)) ->
loop([2|3], Fun(2)) ->
loop([3|], Fun(3)) ->
loop([], _) ->
ok.
U kunt vergelijken met lists:foreach/2
output:
lists:foreach(Fun, [1,2,3]).
Recursieve lus over lijst die gewijzigde lijst retourneert
Nog een handig voorbeeld, vergelijkbaar met lists:map/2
. Deze functie heeft één lijst en één anonieme functie. Telkens wanneer een waarde in de lijst overeenkomt, passen we er een functie op toe.
-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).
Je kunt het zo noemen:
Fun(X) -> X+1 end.
loop([1,2,3], Fun).
Hier de recursieve functie-uitbreiding:
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].
Deze functie wordt ook "staartrecursieve functie" genoemd, omdat we een variabele zoals een accumulator gebruiken om gewijzigde gegevens door te geven aan meerdere uitvoeringscontexten.
Iolist en Bitstring
Zoals lijst, is de eenvoudigste functie over iolist en bitstring :
-spec loop(iolist()) -> ok | {ok, iolist} .
loop(<<>>) ->
ok;
loop(<<Head, Tail/bitstring>>) ->
loop(Tail);
loop(<<Rest/bitstring>>) ->
{ok, Rest}
Je kunt het zo noemen:
loop(<<"abc">>).
Hier de recursieve functie-uitbreiding:
loop(<<"a"/bitstring, "bc"/bitstring>>) ->
loop(<<"b"/bitstring, "c"/bitstring>>) ->
loop(<<"c"/bitstring>>) ->
loop(<<>>) ->
ok.
Recursieve functie via variabele binaire grootte
Deze code neemt bitstring en definieert dynamisch de binaire grootte ervan. Dus als we elke 4
bits een grootte van 4
, worden gegevens gekoppeld. Deze lus doet niets interessants, het is gewoon onze pijler.
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.
Je kunt het zo noemen:
loop(<<"abc">>, 4).
Hier de recursieve functie-uitbreiding:
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.
Onze bitstring is verdeeld over 7 patronen. Waarom? Omdat Erlang standaard een binaire grootte van 8
bits gebruikt, hebben we 4
bits als we het in twee delen. Onze string is 8*3=24
bits. 24/4=6
patronen. Laatste patroon is <<>>
. loop/2
functie wordt 7 keer genoemd.
recursieve functie over variabele binaire grootte met acties
Nu kunnen we interessantere dingen doen. Deze functie heeft nog een argument nodig, een anonieme functie. Telkens wanneer we een patroon matchen, wordt dit aan het patroon doorgegeven.
-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.
Je kunt het zo noemen:
Fun = fun(X) -> io:format("~p~n", [X]) end.
loop(<<"abc">>, 4, Fun).
Hier de recursieve functie-uitbreiding:
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.
Recursieve functie via bitstring retourneert gemodificeerde bitstring
Deze lijkt op lists:map/2
maar voor bitstring en 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.
Deze code lijkt complexer. Twee functies werden toegevoegd: loop/2
en loop/3
. Deze twee functies zijn eenvoudige interface naar loop/4
.
U kunt het als volgt uitvoeren:
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>>
Kaart
Kaart in Erlang is equivalent van hashes in Perl of woordenboeken in Python, het is een sleutel / waardewinkel. Om elke opgeslagen waarde weer te geven, kunt u elke sleutel weergeven en sleutel / waardepaar retourneren. Deze eerste lus geeft u een idee:
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).
Je kunt het zo uitvoeren:
Map = #{1 => "one", 2 => "two", 3 => "three"}.
loop(Map).
% will return:
% 1: "one"
% 2: "two"
% 3: "three"
Beheersstaat
Recursieve functie gebruikt hun status om te herhalen. Wanneer u een nieuw proces voortbrengt, is dit proces eenvoudig een lus met een bepaalde gedefinieerde status.
Anonieme functie
Hier 2 voorbeelden van recursieve anonieme functies op basis van het vorige voorbeeld. Ten eerste, eenvoudige oneindige lus:
InfiniteLoop = fun
R() ->
R() end.
Ten tweede, anonieme functie die lus over lijst doet:
LoopOverList = fun
R([]) -> ok;
R([H|T]) ->
R(T) end.
Deze twee functies kunnen worden herschreven als:
InfiniteLoop = fun loop/0.
In dit geval is loop/0
een verwijzing naar loop/0
uit opmerkingen. Ten tweede, met iets complexer:
LoopOverLlist = fun loop/2.
Hier is loop/2
een verwijzing naar loop/2
uit het lijstvoorbeeld. Deze twee notaties zijn syntactische suiker.