83 lines
2.6 KiB
Markdown
83 lines
2.6 KiB
Markdown
|
---
|
||
|
layout: default
|
||
|
math: true
|
||
|
---
|
||
|
|
||
|
# Macros
|
||
|
|
||
|
En Julia, il est possible de définir des expressions ("quoted expressions") qui sont analysées syntaxiquement ("parsées"),
|
||
|
mais pas évaluées, ce qui est assez pratique pour réutiliser un bout de code régulièrement. Par exemple, on peut a souvent
|
||
|
besoin d'échanger le contenu de deux variables, ce qui peut se faire sans variables intermédiaires avec un ou exclusif\* :
|
||
|
|
||
|
swap = :(
|
||
|
x = x ^ y
|
||
|
y = y ^ x
|
||
|
x = x ^ y
|
||
|
)
|
||
|
|
||
|
\* c'est une [mauvaise idée](https://en.wikipedia.org/wiki/XOR_swap_algorithm#Reasons_for_avoidance_in_practice) en pratique.
|
||
|
|
||
|
On peut alors insérer `eval(swap)` dans notre code pour échanger le contenu de `x` et `y`,
|
||
|
pour calculer la distance entre `x` et `y` de manière totalement inéfficace, par exemple :
|
||
|
|
||
|
|
||
|
function distance(x, y)
|
||
|
if x < y
|
||
|
eval(swap)
|
||
|
end
|
||
|
return x - y
|
||
|
end
|
||
|
|
||
|
|
||
|
Pas très pratique, puis qu'on ne peut faire ça que si les variables qui nous intéressent
|
||
|
sont `x` et `y`. Pour plus de flexibilité, on peut définir une macro, sorte d'expression "quotée"
|
||
|
paramétrée. Pour assigner à un vecteur les coordonnées du k-ième vecteur de la base canonique, ça
|
||
|
donnerait :
|
||
|
|
||
|
macro set_canonical(vector, k)
|
||
|
:( fill!($vector, 0); $vector[$k] = 1 )
|
||
|
end
|
||
|
|
||
|
On peut alors faire quelque chose comme
|
||
|
|
||
|
vector = ones(4)/2;
|
||
|
@set_canonical(vector, 3)
|
||
|
vector
|
||
|
# 4-element Array{Float64,1}:
|
||
|
# 0.0
|
||
|
# 0.0
|
||
|
# 1.0
|
||
|
# 0.0
|
||
|
|
||
|
Les différences avec une fonction :
|
||
|
- le code est remplacé à la compilation, il n'y a pas d'appel.
|
||
|
- le code d'une macro est collé tel quel à l'endroit où elle est utilisée, elle peut faire référence à des variables du contexte parent.
|
||
|
|
||
|
À la différence d'`eval`, une macro est substituée à la compilation.
|
||
|
|
||
|
## Portée des variables et modules
|
||
|
|
||
|
Comme une fonction, une macro peut-être définie dans un module.
|
||
|
|
||
|
# algebre.jl
|
||
|
module algebre
|
||
|
macro set_canonical(vector, k)
|
||
|
:( fill!($vector, 0); $vector[$k] = 1 )
|
||
|
end
|
||
|
end
|
||
|
|
||
|
Mais il faut alors faire attention :
|
||
|
|
||
|
import algebre.jl
|
||
|
|
||
|
vector = zeros(4)
|
||
|
@algebre.set_canonical(vector, 3)
|
||
|
|
||
|
# ERROR: LoadError: UndefVarError: vector not defined
|
||
|
|
||
|
Les variables évaluées dans la macro sont évaluées dans le contexte du module, pas celui où la macro est utilisée. C'est plus compliqué d'une simple copie de code. Comme `vector` n'existe pas dans le contexte du module, d'où l'erreur. Pour palier à ça, on peut utiliser `esc`, qui permet d'accéder au contexte parent depuis un module.
|
||
|
|
||
|
macro set_canonical(vector, k)
|
||
|
esc(:( fill!($vector, 0); $vector[$k] = 1 ))
|
||
|
end
|