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]).
수정 된 목록을 반환하는 목록에 대한 순환 루프
또 다른 유용한 예는 lists:map/2
와 유사합니다. 이 함수는 하나의 목록과 하나의 익명 함수를 취합니다. 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.
가변 바이너리 크기에 걸친 재귀 함수
이 코드는 bitstring을 가져 와서 이진 크기를 동적으로 정의합니다. 만약 우리가의 크기로 설정하면, 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.
우리의 bitstring은 7 패턴 이상 splitted 있습니다. 왜? 기본적으로 Erlang은 8
비트의 바이너리 크기를 사용하기 때문에, 2로 나누면 4
비트가됩니다. 우리의 문자열은 8*3=24
비트입니다. 24/4=6
패턴. 마지막 패턴은 <<>>
입니다. loop/2
함수는 7 번 호출됩니다.
동작이있는 가변 바이너리 크기에 대한 재귀 함수
이제 우리는 더 재미있는 일을 할 수 있습니다. 이 함수는 익명 함수 인 인수를 하나 더 사용합니다. 우리가 패턴과 매치 할 때마다이 패턴이 패턴에 전달됩니다.
-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
와 유사하지만 bitstring과 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.
이 코드는 더 복잡해 보입니다. loop/2
와 loop/3
두 가지 기능이 추가되었습니다. 이 두 함수는 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.
이 두 함수는 다음과 같이 재 작성 될 수 있습니다.
InfiniteLoop = fun loop/0.
이 경우 loop/0
은 비고에서 오는 loop/0
에 대한 참조입니다. 둘째로, 조금 더 복잡한 것은 :
LoopOverLlist = fun loop/2.
여기서, loop/2
에 대한 참조 loop/2
리스트 예의. 이 두 표기법은 통사론적인 설탕입니다.