Les fonctions#

Il est possible de définir une fonction de trois façons différentes :

Définition en ligne#

f(x) = 3x + 1
f (generic function with 1 method)
f(2)
7
g(x,y)=[x*y+1, x-y]
g (generic function with 1 method)
g(1,2)
2-element Vector{Int64}:
  3
 -1

Il est possible d’utiliser begin-end ou ( ; ) pour délimiter la fonction la dernière valeur calculée est retournée

h(x) = begin
    y = 2
    x + y
end
h (generic function with 1 method)
h(3)
5
h(x) = (y=2; x+y) # equivalent à la première écriture
h (generic function with 1 method)
h(3)
5

Structurée#

Julia possède une structure plus classique à l’aide de function-end comme précédemment la dernière valeur calculée est par défaut la variable de retour autrement l’utilisation de return spécifie la ou les variables de sortie.

function h(x,y)
    z = x+y
    return z^2/(abs(x)+1)
end
h (generic function with 2 methods)
h(1,2)
4.5

L’usage de return pour fixer la sortie

function choix(x)
    if x>0
        return "Positif"
    else
        return "Négatif"
    end
end
choix (generic function with 1 method)
txt=choix(3)
"Positif"
choix(x) = x > 0 ? "Positif" : "Négatif"
choix (generic function with 1 method)

Anonyme#

Le concept peut paraître abstrait mais on peut définir une fonction sans la nommer puis l’affecter à une variable…

x -> x^2
#1 (generic function with 1 method)
λ = x -> sqrt(x)
#5 (generic function with 1 method)
λ(1)
1.0
typeof(λ)
var"#5#6"

Arguments de sortie#

Pour avoir plusieurs arguments de sortie il faut utiliser un “tuple” autrement dit une collection d’éléments

function multi_output(x,y)
    return x+y, x-y
end
multi_output (generic function with 1 method)
a = multi_output(1,2) # un seul argument de sortie qui contient un tuple
(3, -1)
typeof(a)
Tuple{Int64, Int64}
a[1]
3
a[2]
-1
a, b = multi_output(1,2) # assignation aux 2 arguments de sortie
(3, -1)
a
3
b
-1

Portée des variables#

Quelle est la portée des variables, autrement dit une variable définie peut elle être accessible, modifiable dans une fonction sans la passer en paramètre ?

a = 1
f(x) = a + x
f(1)
2

Attention, lorsque l’on utilise des variables globales à l’intérieur d’une fonction. La compilation est plus difficile car la fonction doit prendre en compte les modifications éventuelles de la variable a

function ff1(x)
    x + a
end

@code_llvm ff1(1)
;  @ In[30]:1 within `ff1`
define nonnull {}* @julia_ff1_1363(i64 signext %0) #0 {
top:
  %1 = alloca [2 x {}*], align 8
  %gcframe2 = alloca [4 x {}*], align 16
  %gcframe2.sub = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe2, i64 0, i64 0
  %.sub = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 0
  %2 = bitcast [4 x {}*]* %gcframe2 to i8*
  call void @llvm.memset.p0i8.i64(i8* align 16 %2, i8 0, i64 32, i1 true)
  %thread_ptr = call i8* asm "movq %fs:0, $0", "=r"() #6
  %tls_ppgcstack = getelementptr i8, i8* %thread_ptr, i64 -8
  %3 = bitcast i8* %tls_ppgcstack to {}****
  %tls_pgcstack = load {}***, {}**** %3, align 8
;  @ In[30]:2 within `ff1`
  %4 = bitcast [4 x {}*]* %gcframe2 to i64*
  store i64 8, i64* %4, align 16
  %5 = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe2, i64 0, i64 1
  %6 = bitcast {}** %5 to {}***
  %7 = load {}**, {}*** %tls_pgcstack, align 8
  store {}** %7, {}*** %6, align 8
  %8 = bitcast {}*** %tls_pgcstack to {}***
  store {}** %gcframe2.sub, {}*** %8, align 8
  %a = load atomic {}*, {}** inttoptr (i64 140122390545264 to {}**) unordered, align 16
  %9 = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe2, i64 0, i64 2
  store {}* %a, {}** %9, align 16
  %10 = call nonnull {}* @ijl_box_int64(i64 signext %0)
  %11 = getelementptr inbounds [4 x {}*], [4 x {}*]* %gcframe2, i64 0, i64 3
  store {}* %10, {}** %11, align 8
  store {}* %10, {}** %.sub, align 8
  %12 = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 1
  store {}* %a, {}** %12, align 8
  %13 = call nonnull {}* @ijl_apply_generic({}* inttoptr (i64 140122214190464 to {}*), {}** nonnull %.sub, i32 2)
  %14 = load {}*, {}** %5, align 8
  %15 = bitcast {}*** %tls_pgcstack to {}**
  store {}* %14, {}** %15, align 8
  ret {}* %13
}

Lorsqu’une variable est globale il est conseillé de lui donner un caractère constant. Le compilateur vous remerciera dans ce cas.

const α = 1
function ff2(x)
    x + α
end

@code_llvm ff2(1)
;  @ In[31]:2 within `ff2`
define i64 @julia_ff2_1388(i64 signext %0) #0 {
top:
;  @ In[31]:3 within `ff2`
; ┌ @ int.jl:87 within `+`
   %1 = add i64 %0, 1
; └
  ret i64 %1
}
@code_lowered ff2(1)
CodeInfo(
1 ─ %1 = x + Main.α
└──      return %1
)

Donc par défaut une variable définie est connue et utilisable par toute fonction appelée (de même à l’intérieur d’une fonction).

Si on redéfinit localement dans la fonction la variable alors “elle écrase localement” la dite variable et en sortie de fonction rien n’est modifié.

Attention à l’utilisation dans la fonction d’une variable extérieure puis d’affecter une valeur à cette variable…

Le mapping#

Souvent on écrit une fonction sous une forme scalaire sans chercher à utiliser les opérations vectorielles pour appliquer cette fonction à un tableau

f(x) = x^2 + 1
f(1:5)  # il aurait fallu définir f(x)=x.^2.+1
MethodError: no method matching ^(::UnitRange{Int64}, ::Int64)

Closest candidates are:
  ^(::Float16, ::Integer)
   @ Base math.jl:1283
  ^(::Regex, ::Integer)
   @ Base regex.jl:863
  ^(::Float32, ::Integer)
   @ Base math.jl:1277
  ...


Stacktrace:
 [1] literal_pow
   @ ./intfuncs.jl:351 [inlined]
 [2] f(x::UnitRange{Int64})
   @ Main ./In[33]:1
 [3] top-level scope
   @ In[33]:2

La fonction map permet de palier à ce manquement

map(f,1:5)
5-element Vector{Int64}:
  2
  5
 10
 17
 26
f.(1:5)
5-element Vector{Int64}:
  2
  5
 10
 17
 26
v = [1 2; 3 4]
map(f,v)
2×2 Matrix{Int64}:
  2   5
 10  17
reduce(+, v )
10
sum(v)
10
f.([1 2; 3 4])
2×2 Matrix{Int64}:
  2   5
 10  17
g(x,y)=x+y
map(g,0:3,1:4)
4-element Vector{Int64}:
 1
 3
 5
 7
map(g,[1 2;3 4],[2 3;4 5])
2×2 Matrix{Int64}:
 3  5
 7  9
g(f,x)=f(x)+1
g (generic function with 1 method)
g(sin,1)
1.8414709848078965

Il existe également un opérateur pour “composer” les fonctions \circ

f1(x) = 2x + 1
f2(x) = 1 - 4x
(f1  f2)(3)
-21
f1(f2(3))
-21