diff --git a/notes/julia/index.markdown b/notes/julia/index.markdown new file mode 100644 index 0000000..fda77ba --- /dev/null +++ b/notes/julia/index.markdown @@ -0,0 +1,82 @@ +--- +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