Elixir Language
Les fonctions
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