Recherche…


Syntaxe

  • fonction (liste | iolist | tuple) -> fonction (queue).

Remarques

Pourquoi des fonctions récursives?

Erlang est un langage de programmation fonctionnel et ne comporte aucun type de structure en boucle. Tout dans la programmation fonctionnelle est basé sur des données, des types et des fonctions. Si vous voulez une boucle, vous devez créer une fonction qui s'appelle elle-même.

Les while traditionnels ou for boucle dans un langage impératif et orienté objet peuvent être représentés comme cela dans Erlang:

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

Une bonne méthode pour comprendre ce concept consiste à développer tous les appels de fonctions. Nous verrons cela sur d'autres exemples.

liste

Voici la fonction récursive la plus simple sur le type de liste . Cette fonction ne fait que naviguer dans une liste depuis son début jusqu'à sa fin et ne fait plus rien.

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

Vous pouvez l'appeler comme ceci:

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

Voici l'expansion de la fonction récursive:

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

Boucle récursive avec des actions IO

Le code précédent ne fait rien et est plutôt inutile. Nous allons donc créer maintenant une fonction récursive qui exécute certaines actions. Ce code est similaire aux lists:foreach/2 .

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

Vous pouvez l'appeler comme ceci:

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

Voici l'expansion de la fonction récursive:

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

Vous pouvez comparer avec des lists:foreach/2 sortie:

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

Liste récursive sur la liste renvoyant la liste modifiée

Un autre exemple utile, similaire aux lists:map/2 . Cette fonction prendra une liste et une fonction anonyme. Chaque fois qu'une valeur dans la liste est appariée, nous lui appliquons une fonction.

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

Vous pouvez l'appeler comme ceci:

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

Voici l'expansion de la fonction récursive:

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

Cette fonction est également appelée "fonction récursive de la queue", car nous utilisons une variable comme un accumulateur pour transmettre des données modifiées sur plusieurs contextes d’exécution.

Iolist et Bitstring

Comme la liste, la fonction la plus simple sur iolist et bitstring est:

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

Vous pouvez l'appeler comme ceci:

loop(<<"abc">>).

Voici l'expansion de la fonction récursive:

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

Fonction récursive sur la taille binaire variable

Ce code prend la chaîne de bits et définit dynamiquement sa taille binaire. Donc, si nous définissons une taille de 4 , tous les 4 bits, une donnée sera mise en correspondance. Cette boucle ne fait rien d'intéressant, c'est juste notre pilier.

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.

Vous pouvez l'appeler comme ceci:

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

Voici l'expansion de la fonction récursive:

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.

Notre chaîne de bits est divisée en 7 motifs. Pourquoi? Parce que par défaut, Erlang utilise une taille binaire de 8 bits, si nous le divisons en deux, nous avons 4 bits. Notre chaîne est 8*3=24 bits. 24/4=6 motifs. Le dernier modèle est <<>> . loop/2 est appelée 7 fois.

fonction récursive sur la taille binaire variable avec des actions

Maintenant, nous pouvons faire quelque chose de plus intéressant. Cette fonction prend un argument de plus, une fonction anonyme. Chaque fois que nous faisons correspondre un motif, celui-ci lui sera transmis.

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

Vous pouvez l'appeler comme ceci:

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

Voici l'expansion de la fonction récursive:

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.

Fonction récursive sur la chaîne de bits renvoyant la chaîne de bits modifiée

Celui-ci est similaire aux lists:map/2 mais pour bitstring et 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.

Ce code semble plus complexe. Deux fonctions ont été ajoutées: loop/2 et loop/3 . Ces deux fonctions sont une interface simple à loop/4 .

Vous pouvez l'exécuter comme ceci:

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

Carte

Map in Erlang est équivalent aux hashes dans Perl ou aux dictionnaires dans Python, c'est un magasin de clé / valeur. Pour répertorier toutes les valeurs stockées, vous pouvez répertorier chaque clé et renvoyer une paire clé / valeur. Cette première boucle vous donne une idée:

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

Vous pouvez l'exécuter comme ça:

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

Etat de gestion

La fonction récursive utilise leurs états pour boucler. Lorsque vous créez un nouveau processus, ce processus sera simplement une boucle avec un état défini.

Fonction anonyme

Voici 2 exemples de fonctions anonymes récursives basées sur l'exemple précédent. Tout d'abord, une boucle infinie simple:

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

Deuxièmement, la fonction anonyme fait la liste en boucle:

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

Ces deux fonctions peuvent être réécrites comme suit:

InfiniteLoop = fun loop/0.

Dans ce cas, loop/0 est une référence à loop/0 des remarques. Deuxièmement, avec un peu plus complexe:

LoopOverLlist = fun loop/2.

Ici, loop/2 est une référence à loop/2 de la liste exemple. Ces deux notations sont le sucre syntaxique.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow