[notes] import julia (macros)
This commit is contained in:
parent
a0e326a437
commit
5968e5fadf
1 changed files with 82 additions and 0 deletions
82
notes/julia/index.markdown
Normal file
82
notes/julia/index.markdown
Normal file
|
@ -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
|
Loading…
Reference in a new issue