Erlang Language
ループと再帰
サーチ…
構文
- function(list | iolist | tuple) - > function(tail)。
備考
なぜ再帰関数ですか?
Erlangは関数型プログラミング言語であり、どのようなループ構造でもありません。関数型プログラミングのすべては、データ、型、関数に基づいています。ループが必要な場合は、自分自身を呼び出す関数を作成する必要があります。
Erlangでは、 while
やfor
ループのような命令型言語やオブジェクト指向言語を表すことができます。
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
と似てい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
と比較することができlists:foreach/2
出力:
lists:foreach(Fun, [1,2,3]).
リストを返す再帰ループ
もう1つの有用な例は、 lists:map/2
に似ていlists:map/2
。この関数は1つのリストと1つの無名関数を取ります。 listの値が一致するたびに関数を適用します。
-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と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.
可変バイナリサイズに対する再帰関数
このコードはビットストリングを取り、バイナリサイズを動的に定義します。もしそうであれば、我々はのサイズに設定した場合、 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
ビットのバイナリサイズを使用するので、2つに分割すると4
ビットになります。私たちの文字列は8*3=24
ビットです。 24/4=6
パターン。最後のパターンは<<>>
です。 loop/2
関数は7回呼び出されます。
アクションによる可変バイナリサイズに対する再帰関数
さて、もっと面白いことをすることができます。この関数はもう1つの引数anonymous関数をとります。パターンにマッチするたびに、このパターンがパターンに渡されます。
-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.
修正されたビットストリングを返すビットストリング上の再帰関数
これはlists:map/2
似ていlists:map/2
が、bitstringとiolistに似ていlists:map/2
。
% 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/2
つの機能が追加されました。これらの2つの機能は、 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のMapは、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.
これらの2つの機能は、次のように書き直すことができます。
InfiniteLoop = fun loop/0.
この場合、 loop/0
は備考からのloop/0
への参照です。第二に、少し複雑になります。
LoopOverLlist = fun loop/2.
ここで、 loop/2
は、リストの例からのloop/2
への参照です。これらの2つの表記は統語的な砂糖です。