Le concept Basics a présenté deux façons de définir une fonction.
Dans le cas le plus général, la forme multiligne :
function muladd(x, y, z)
x * y + z
end
La forme « affectation », ou forme « sur une seule ligne », pour les définitions courtes :
muladd(x, y, z) = x * y + z
Dans une troisième forme, encore plus courte, il est possible de créer une fonction brève, à usage unique, sans lui donner de nom :
julia> map(x -> 2x, 1:3)
4-element Vector{Int64}:
2
4
6
julia> map((x, y) -> x * y, 1:3, 4:6)
3-element Vector{Int64}:
4
10
18
Ici, x -> 2x est une « fonction anonyme ». Cela correspond à ce que d'autres langages appellent une « fonction lambda ».
À noter que plusieurs arguments nécessitent des parenthèses, comme dans (x, y) -> x * y.
Les fonctions anonymes sont courantes dans le code Julia, surtout lorsqu'elles sont combinées à des fonctions d'ordre supérieur comme map() et filter().
Jusqu'ici dans le programme, on n'a vu que des fonctions qui prennent un nombre précis d'arguments et qui exigent que tout appel leur fournisse tous ces arguments, dans le bon ordre. Ce serait limitant et peu pratique, c'est pourquoi il existe plusieurs autres possibilités.
Comme beaucoup de langages, Julia permet de préciser des valeurs par défaut pour certains arguments dans la définition d'une fonction.
L'appel de fonction peut alors soit fournir une valeur pour cet argument, soit l'omettre et se fier à la valeur par défaut.
julia> f(x, y=10) = x * y
f (generic function with 2 methods)
julia> f(2, 3)
6
julia> f(2)
20
Tous les arguments sans valeur par défaut doivent venir avant les arguments avec valeur par défaut, ce qui signifie que f(x=2, y) serait invalide.
Tous les exemples jusqu'ici utilisent des positional arguments, où les valeurs fournies dans un appel de fonction doivent correspondre à l'ordre des arguments correspondants dans la définition de la fonction.
Comme beaucoup de langages, Julia permet aussi des keyword arguments.
Les appels de fonction doivent préciser le nom de l'argument, mais plusieurs arguments nommés peuvent ensuite être indiqués dans n'importe quel ordre.
Une particularité de Julia est que les arguments nommés (s'il y en a) dans la définition de la fonction doivent être précédés d'un point-virgule ; pour les séparer des éventuels arguments positionnels.
Un appel de fonction peut utiliser soit ; soit , entre le dernier argument positionnel et le premier argument nommé.
julia> b(x; y) = x + y
b (generic function with 1 method)
julia> b(2, y=3)
5
# keyword is required when calling
julia> b(2, 3)
ERROR: MethodError: no method matching b(::Int64, ::Int64)
The function `b` exists, but no method is defined for this combination of argument types.
Des valeurs par défaut peuvent éventuellement être précisées, exactement comme pour les arguments positionnels.
Il est courant d'aboutir à une syntaxe comme myarg=myarg dans un appel de fonction, quand une variable portant le même nom que le paramètre a été calculée au préalable.
Une syntaxe abrégée est autorisée dans ce cas :
julia> width = 4.0
4.0
julia> height = √ width
2.0
julia> area(; width, height) = width * height
area (generic function with 1 method)
# repetition
julia> area(; width=width, height=height)
8.0
# shorthand form
julia> area(; width, height)
8.0
Ce sont les noms standards d'un aspect bien utile de la syntaxe de Julia, au cas où tu te posais la question.
Tous les deux désignent l'opérateur ....
Le splatting s'utilise dans les appels de fonction, pour étendre une collection en valeurs individuelles attendues par la fonction.
C'est peut-être plus facile à montrer qu'à expliquer :
julia> fxyz(x, y, z) = x * y * z
fxyz (generic function with 1 method)
julia> xyz = [2, 3, 4]
3-element Vector{Int64}:
2
3
4
# Using the vector directly in a function call is invalid
julia> fxyz(xyz)
ERROR: MethodError: no method matching fxyz(::Vector{Int64})
The function `fxyz` exists, but no method is defined for this combination of argument types.
# splatting converts the vector to 3 numbers, used as positional arguments
julia> fxyz(xyz...)
24
Certains « appels de fonction » sont masqués par du sucre syntaxique, donc le splatting peut aussi s'utiliser de manières moins évidentes.
Par exemple, l'affectation multiple utilise en interne une fonction de construction de tuple :
julia> first, rest... = [1, 2, 3, 4]
4-element Vector{Int64}:
1
2
3
4
julia> first
1
julia> rest
3-element Vector{Int64}:
2
3
4
Les arguments nommés peuvent aussi être fournis par splatting, généralement à l'aide d'un named tuple.
Un Dict fonctionne également, mais les clés doivent être des symboles (les chaînes de caractères ne fonctionnent pas ici).
# function with 3 keyword arguments
julia> fabc(; a, b, c) = a + b + c
fabc (generic function with 1 method)
# named tuple
julia> abc_nt = (a=2, b=3, c=4)
(a = 2, b = 3, c = 4)
# there are no positional arguments, so need to use ; before kw argument
julia> fabc(;abc_nt...)
9
# Dict
julia> abc_dict = Dict(:a=>2, :b=>3, :c=>4)
Dict{Symbol, Int64} with 3 entries:
:a => 2
:b => 3
:c => 4
julia> fabc(;abc_dict...)
9
Le slurping s'utilise dans la définition de la fonction, pour regrouper un nombre arbitraire de valeurs individuelles dans une collection.
julia> f_more(i, j, more...) = i + j + sum(more)
f_more (generic function with 1 method)
julia> f_more(1, 3, 5, 7, 9, 11)
36
Le nom de l'argument slurpé (ici more) n'a pas d'importance.
Le type de cette variable est choisi par le compilateur, mais pour des arguments positionnels, ce sera probablement un tuple ou quelque chose d'approchant.
Les arguments nommés peuvent aussi être slurpés, ce qui donne un Dict (ou équivalent).
julia> f_kwslurp(x, y; switches...) = :mult in keys(switches) ? x * y : x + y
f_kwslurp (generic function with 1 method)
julia> f_kwslurp(5, 6; mult=true)
30
julia> f_kwslurp(5, 6)
11
N'importe quel argument nommé peut être utilisé dans l'appel. C'est à la définition de la fonction de décider à quels arguments nommés répondre et lesquels ignorer.