homepage/notes/julia/index.markdown

83 lines
2.6 KiB
Markdown
Raw Normal View History

2016-04-21 11:51:36 +02:00
---
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