Recherche…


Fonctions anonymes

Dans Elixir, une pratique courante consiste à utiliser des fonctions anonymes. Créer une fonction anonyme est simple:

iex(1)> my_func = fn x -> x * 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>

La syntaxe générale est la suivante:

fn args -> output end

Pour plus de lisibilité, vous pouvez mettre des parenthèses autour des arguments:

iex(2)> my_func = fn (x, y) -> x*y end
#Function<12.52032458/2 in :erl_eval.expr/5>

Pour appeler une fonction anonyme, appelez-la par le nom attribué et ajoutez . entre le nom et les arguments.

iex(3)>my_func.(7, 5)
35

Il est possible de déclarer des fonctions anonymes sans arguments:

iex(4)> my_func2 = fn -> IO.puts "hello there" end
iex(5)> my_func2.()
hello there
:ok

Utilisation de l'opérateur de capture

Pour rendre les fonctions anonymes plus concises, vous pouvez utiliser l' opérateur de capture & . Par exemple, au lieu de:

iex(5)> my_func = fn (x) -> x*x*x end

Tu peux écrire:

iex(6)> my_func = &(&1*&1*&1)

Avec plusieurs paramètres, utilisez le nombre correspondant à chaque argument, en partant de 1 :

iex(7)> my_func = fn (x, y) -> x + y end

iex(8)> my_func = &(&1 + &2)   # &1 stands for x and &2 stands for y

iex(9)> my_func.(4, 5)
9

Plusieurs corps

Une fonction anonyme peut également avoir plusieurs corps (à la suite d' une correspondance de modèle ):

my_func = fn
  param1 -> do_this
  param2 -> do_that
end

Lorsque vous appelez une fonction avec plusieurs corps, Elixir tente de faire correspondre les paramètres fournis avec le corps de la fonction approprié.

Listes de mots clés en tant que paramètres de fonction

Utilisez des listes de mots-clés pour les paramètres de style "options" contenant plusieurs paires clé-valeur:

def myfunc(arg1, opts \\ []) do
  # Function body
end

Nous pouvons appeler la fonction ci-dessus ainsi:

iex> myfunc "hello", pizza: true, soda: false

ce qui équivaut à:

iex> myfunc("hello", [pizza: true, soda: false])

Les valeurs d'argument sont disponibles respectivement sous la forme opts.pizza et opts.soda .
Vous pouvez également utiliser des atomes: opts[:pizza] et opts[:soda] .

Fonctions nommées et fonctions privées

Fonctions nommées

defmodule Math do
    # one way
    def add(a, b) do
        a + b
    end

    # another way
    def subtract(a, b), do: a - b
end

iex> Math.add(2, 3)
5
:ok
iex> Math.subtract(5, 2)
3
:ok

Fonctions Privées

defmodule Math do
    def sum(a, b) do
        add(a, b)
    end

    # Private Function
    defp add(a, b) do
        a + b
    end
end

iex> Math.add(2, 3)
** (UndefinedFunctionError) undefined function Math.add/2
Math.add(3, 4)
iex> Math.sum(2, 3)
5

Correspondance de motif

Elixir correspond à un appel de fonction à son corps en fonction de la valeur de ses arguments.

defmodule Math do
    def factorial(0): do: 1
    def factorial(n): do: n * factorial(n - 1)
end

Ici, la factorielle des nombres positifs correspond à la deuxième clause, tandis que la factorial(0) correspond à la première. (en ignorant les nombres négatifs pour des raisons de simplicité). Elixir essaie de faire correspondre les fonctions de haut en bas. Si la deuxième fonction est écrite au-dessus du premier, nous obtiendrons un résultat inattendu à mesure qu’il se dirigera vers une récursion sans fin. Parce que factorial(0) correspond à factorial(n)

Clauses de garde

Les clauses de garde nous permettent de vérifier les arguments avant d’exécuter la fonction. Les clauses de garde sont généralement préférées à if et cond raison de leur lisibilité, et pour faciliter une certaine technique d'optimisation pour le compilateur. La première définition de fonction où tous les gardes correspondent est exécutée.

Voici un exemple d'implémentation de la fonction factorielle à l'aide de gardes et de correspondance de modèle.

defmodule Math do
    def factorial(0), do: 1
    def factorial(n) when n > 0: do: n * factorial(n - 1)
end

Le premier motif correspond si (et seulement si) l'argument est 0 . Si l'argument n'est pas 0 , la correspondance du modèle échoue et la fonction suivante ci-dessous est cochée.

Cette deuxième définition de fonction a une clause de garde: when n > 0 . Cela signifie que cette fonction ne correspond que si l'argument n est supérieur à 0 . Après tout, la fonction factorielle mathématique n'est pas définie pour les entiers négatifs.

Si aucune définition de fonction (y compris leur correspondance de modèle et les clauses de garde) ne correspond, une FunctionClauseError sera déclenchée. Cela se produit pour cette fonction lorsque nous passons un nombre négatif comme argument, car il n'est pas défini pour les nombres négatifs.

Notez que cette FunctionClauseError n'est pas une erreur. Renvoyer -1 ou 0 ou une autre "valeur d'erreur" comme cela est courant dans certaines autres langues cacherait le fait que vous avez appelé une fonction indéfinie, en masquant la source de l'erreur, créant éventuellement un énorme bogue douloureux pour un futur développeur.

Paramètres par défaut

Vous pouvez transmettre les paramètres par défaut à toute fonction nommée en utilisant la syntaxe suivante: param \\ value :

defmodule Example do
    def func(p1, p2 \\ 2) do
        IO.inspect [p1, p2]
    end
end

Example.func("a")    # => ["a", 2]
Example.func("b", 4) # => ["b", 4]

Fonctions de capture

Utilisez & pour capturer les fonctions des autres modules. Vous pouvez utiliser les fonctions capturées directement en tant que paramètres de fonction ou dans des fonctions anonymes.

Enum.map(list, fn(x) -> String.capitalize(x) end)

Peut être rendu plus concis en utilisant & :

Enum.map(list, &String.capitalize(&1))

La capture de fonctions sans passer d’argument nécessite de spécifier explicitement son arité, par exemple &String.capitalize/1 :

defmodule Bob do
  def say(message, f \\ &String.capitalize/1) do
    f.(message)
  end
end


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