Erlang – exemple 04

Cet exemple propose quelques exemples d'utilisation de funs avec Erlang.

Remarques Code

Un fun est une variable contenant une fonction. Dans l'exemple à droite, la variable Somme se fait associer à une fonction d'arité 2 qui évaluera la somme de ses paramètres.

Le mot clé fun permet de créer une telle fonction.

Erlang R14B (erts-5.8.1.1) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.8.1.1  (abort with ^G)
1> Somme = fun(X,Y) -> X + Y end.
#Fun<erl_eval.12.113037538>
2> Somme(2,3).
5
3>

Un fun peut être spécialisée sur la base de divers Patterns, comme le peuvent d'ailleurs les fonctions.

Remarques Code

Par exemple, à droite, le fun associé à Conv a une arité de 3 et réalise une conversion sur la base des atomes servant en tant que deux premiers paramètres.

L'appel Conv(lbs,kg,200). exprime une conversion de 200 lbs en Kg.

Erlang R14B (erts-5.8.1.1) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.8.1.1  (abort with ^G)
1> Conv = fun(lbs,kg,X) -> X/2.2;
1>           (kg,lbs,X) -> X*2.2 end.
#Fun<erl_eval.18.105910772>
2> Conv(lbs,kg,200).
90.9090909090909
3>

Il est aussi possible d'écrire un fun qui retourne un fun, évidemment.

Remarques Code

Un exemple inspiré directement du livre Programming Erlang, de Joe Armstrong (l'auteur du langage lui-même), est le fun associé à Fois, qui prend un facteur et retourne un fun capable de multiplier ce facteur par une valeur.

Tripler correspond à Fois(3), donc Tripler(5) multiplie 5 par le facteur 3.

Erlang R14B (erts-5.8.1.1) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.8.1.1  (abort with ^G)
1> Fois = fun(N) -> (fun(X) -> X * N end) end.
#Fun<erl_eval.6.13229925>
2> Tripler = Fois(3).
#Fun<erl_eval.6.13229925>
3> Tripler(5).
15
4> 

On peut combiner des opérations sur des listes et des fun pour réaliser des opérations semblables à un for_each comme on en voit dans plusieurs langages.

Remarques Code

Par exemple, le code à droite montre une implémentation possible de for_each/2 appliquant une fonction aux éléments d'une liste.

Le cas général reçoit la liste [Tete|Queue] et la fonction Fct, et produit une liste contenant Fct(Tete) suivi de l'application de Fct aux éléments de Queue. L'application de quoi que ce soit à une liste vide résulte en une liste vide.

-module(funs).
-export([for_each/2]).

for_each([Tete|Queue], Fct) ->
   [Fct(Tete)|for_each(Queue,Fct)];
for_each([],_) ->
   [].

Dans la langue des langages fonctionnels, pour ainsi dire, l'opération décrite par for_each/2 ci-dessus est ce qu'on appelle une opération map, qui applique une opération à tous les éléments d'une séquence.

Voyons maintenant un exemple combinant un fun comme Tripler et une fonction comme for_each/2.

Remarques Code

Le code à droite montre comment for_each/2 du module funs peut être utilisé pour appliquer Tripler à chaque élément de la liste [1,2,3,4,5].

Vous conviendrez que c'est à la fois simple et élégant.

Erlang R14B (erts-5.8.1.1) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.8.1.1  (abort with ^G)
1> Fois = fun(N) -> (fun(X) -> X * N end) end.
#Fun<erl_eval.6.13229925>
2> Tripler = Fois(3).
#Fun<erl_eval.6.13229925>
3>  c(funs).
{ok,funs}
4> funs:for_each([1,2,3,4,5], Tripler).
[3,6,9,12,15]
5> 

Enfin, à titre illustratif, il est possible de simuler en Erlang des structures de contrôle qu'on trouve dans des langages impératifs comme C ou C++ à l'aide de fonctions ou de funs.

Remarques Code

Raffinons pour ce faire le code du module funs pour y ajouter une fonction for/3 dont la structure rappelle celle des répétitives for de langages à accolades.

Pour les fins de l'exemple, for(I,Max,F) opérera strictement sur les indices dans l'intervalle inclusif I..Max et appliquera F à chacun de ces indices.

Toujours pour les fins de l'exemple, le fruit de for/3 sera une liste de résultats, mais on aurait pu exprimer le tout autrement.

-module(funs).
-export([for_each/2,for/3]).

for_each([Tete|Queue], Fct) ->
   [Fct(Tete)|for_each(Queue,Fct)];
for_each([],_) ->
   [].

for(Max,Max,Fct) ->
   [Fct(Max)];
for(I,Max,Fct) ->
   [Fct(I)|for(I+1,Max,Fct)].
Remarques Code

L'appel de funs:for/3 à droite applique le fun nommé Tripler (plus haut) à tous les indices entre 1 et 10 inclusivement.

...
5> c(funs).                       
{ok,funs}
6> funs:for(1,10, Tripler).
[3,6,9,12,15,18,21,24,27,30]
7> 

Valid XHTML 1.0 Transitional

CSS Valide !