Erlang Language
Schleife und Rekursion
Suche…
Syntax
- Funktion (Liste | iolist | Tupel) -> Funktion (Schwanz).
Bemerkungen
Warum rekursive Funktionen?
Erlang ist eine funktionale Programmiersprache und keine Art von Schleifenstruktur. Alles in der Funktionsprogrammierung basiert auf Daten, Typ und Funktionen. Wenn Sie eine Schleife wünschen, müssen Sie eine Funktion erstellen, die sich selbst aufruft.
Traditionelle while
oder for
Schleife in imperativer und objektorientierter Sprache können wie in Erlang dargestellt werden
loop() ->
% do something here
loop().
Eine gute Methode, um dieses Konzept zu verstehen, besteht darin, alle Funktionsaufrufe zu erweitern. Wir werden das an anderen Beispielen sehen.
Liste
Hier haben die einfachste rekursive Funktion über Liste Typ. Diese Funktion navigiert nur von Anfang bis Ende in eine Liste und führt nichts weiter aus.
-spec loop(list()) -> ok.
loop([]) ->
ok;
loop([H|T]) ->
loop(T).
Man kann es so nennen:
loop([1,2,3]). % will return ok.
Hier die rekursive Funktionserweiterung:
loop([1|2,3]) ->
loop([2|3]) ->
loop([3|]) ->
loop([]) ->
ok.
Rekursive Schleife mit IO-Aktionen
Vorheriger Code macht nichts und ist ziemlich nutzlos. Wir erstellen also eine rekursive Funktion, die einige Aktionen ausführt. Dieser Code ähnelt lists:foreach/2
.
-spec loop(list(), fun()) -> ok.
loop([], _) ->
ok;
loop([H|T], Fun)
when is_function(Fun) ->
Fun(H),
loop(T, Fun).
Man kann es so nennen:
Fun = fun(X) -> io:format("~p", [X]) end.
loop([1,2,3]).
Hier die rekursive Funktionserweiterung:
loop([1|2,3], Fun(1)) ->
loop([2|3], Fun(2)) ->
loop([3|], Fun(3)) ->
loop([], _) ->
ok.
Sie können mit lists:foreach/2
vergleichen lists:foreach/2
output:
lists:foreach(Fun, [1,2,3]).
Rekursive Schleife über Liste, die die modifizierte Liste zurückgibt
Ein weiteres nützliches Beispiel, ähnlich wie lists:map/2
. Diese Funktion benötigt eine Liste und eine anonyme Funktion. Jedes Mal, wenn ein Wert in der Liste übereinstimmt, wenden wir eine Funktion darauf an.
-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).
Man kann es so nennen:
Fun(X) -> X+1 end.
loop([1,2,3], Fun).
Hier die rekursive Funktionserweiterung:
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].
Diese Funktion wird auch als "rekursive Funktion" bezeichnet, da wir eine Variable wie einen Akkumulator verwenden, um modifizierte Daten über mehrere Ausführungskontexte zu übergeben.
Iolist und Bitstring
Wie Liste ist die einfachste Funktion über Iolist und Bitstring :
-spec loop(iolist()) -> ok | {ok, iolist} .
loop(<<>>) ->
ok;
loop(<<Head, Tail/bitstring>>) ->
loop(Tail);
loop(<<Rest/bitstring>>) ->
{ok, Rest}
Man kann es so nennen:
loop(<<"abc">>).
Hier die rekursive Funktionserweiterung:
loop(<<"a"/bitstring, "bc"/bitstring>>) ->
loop(<<"b"/bitstring, "c"/bitstring>>) ->
loop(<<"c"/bitstring>>) ->
loop(<<>>) ->
ok.
Rekursive Funktion über variable Binärgröße
Dieser Code nimmt Bitstring und definiert dynamisch die binäre Größe davon. Wenn wir also alle 4
Bits eine Größe von 4
festlegen, werden die Daten abgeglichen. Diese Schleife macht nichts Interessantes, es ist nur unsere Säule.
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.
Man kann es so nennen:
loop(<<"abc">>, 4).
Hier die rekursive Funktionserweiterung:
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.
Unsere Bitkette ist auf 7 Muster aufgeteilt. Warum? Da Erlang standardmäßig eine binäre Größe von 8
Bits verwendet, haben wir 4
Bits, wenn wir ihn in zwei Teile teilen. Unsere Zeichenfolge ist 8*3=24
Bit. 24/4=6
Muster. Letztes Muster ist <<>>
. loop/2
Funktion loop/2
wird 7 mal aufgerufen.
Rekursive Funktion über variable Binärgröße mit Aktionen
Jetzt können wir etwas Interessantes tun. Diese Funktion benötigt ein weiteres Argument, eine anonyme Funktion. Jedes Mal, wenn wir ein Muster zuordnen, wird dieses Muster weitergegeben.
-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.
Man kann es so nennen:
Fun = fun(X) -> io:format("~p~n", [X]) end.
loop(<<"abc">>, 4, Fun).
Hier die rekursive Funktionserweiterung:
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.
Rekursive Funktion über Bitstring, die modifizierte Bitstring zurückgibt
Diese lists:map/2
ähnelt lists:map/2
jedoch für Bitstring und 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.
Dieser Code erscheint komplexer. Zwei Funktionen wurden hinzugefügt: loop/2
und loop/3
. Diese beiden Funktionen sind eine einfache Schnittstelle zu loop/4
.
Sie können es so ausführen:
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>>
Karte
Map in Erlang entspricht Hashes in Perl oder Wörterbüchern in Python. Dies ist ein Schlüssel- / Wertspeicher. Um alle gespeicherten Werte aufzulisten, können Sie jeden Schlüssel auflisten und das Schlüssel / Wert-Paar zurückgeben. Diese erste Schleife gibt Ihnen eine 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).
Sie können es so ausführen:
Map = #{1 => "one", 2 => "two", 3 => "three"}.
loop(Map).
% will return:
% 1: "one"
% 2: "two"
% 3: "three"
Verwaltungsstaat
Rekursive Funktionen verwenden ihre Zustände für die Schleife. Wenn Sie einen neuen Prozess erzeugen, ist dieser Prozess einfach eine Schleife mit einem definierten Status.
Anonyme Funktion
Hier zwei Beispiele für rekursive anonyme Funktionen, die auf dem vorherigen Beispiel basieren. Erstens, einfache Endlosschleife:
InfiniteLoop = fun
R() ->
R() end.
Zweitens, eine anonyme Funktion, die eine Schleife über die Liste macht:
LoopOverList = fun
R([]) -> ok;
R([H|T]) ->
R(T) end.
Diese beiden Funktionen könnten wie folgt umgeschrieben werden:
InfiniteLoop = fun loop/0.
In diesem Fall ist loop/0
eine Referenz auf loop/0
aus den Bemerkungen. Zweitens mit etwas komplexer:
LoopOverLlist = fun loop/2.
loop/2
ist hier eine Referenz auf loop/2
aus dem Listenbeispiel. Diese beiden Notationen sind syntaktischer Zucker.