Base.Cartesian
Le module (non exporté) Cartesian fournit des macros qui facilitent l'écriture d'algorithmes multidimensionnels. Le plus souvent, vous pouvez écrire de tels algorithmes avec straightforward techniques; cependant, il existe quelques cas où Base.Cartesian
est encore utile ou nécessaire.
Principles of usage
Un exemple simple d'utilisation est :
@nloops 3 i A begin
s += @nref 3 A i
end
qui génère le code suivant :
for i_3 = axes(A, 3)
for i_2 = axes(A, 2)
for i_1 = axes(A, 1)
s += A[i_1, i_2, i_3]
end
end
end
En général, Cartesian vous permet d'écrire du code générique contenant des éléments répétitifs, comme les boucles imbriquées dans cet exemple. D'autres applications incluent des expressions répétées (par exemple, le déroulement de boucle) ou la création d'appels de fonction avec un nombre variable d'arguments sans utiliser la construction "splat" (i...
).
Basic syntax
La syntaxe (de base) de @nloops
est la suivante :
- Le premier argument doit être un entier (pas une variable) spécifiant le nombre de boucles.
- Le deuxième argument est le préfixe de symbole utilisé pour la variable d'itération. Ici, nous avons utilisé
i
, et les variablesi_1, i_2, i_3
ont été générées. - Le troisième argument spécifie la plage pour chaque variable d'itérateur. Si vous utilisez une variable (symbole) ici, elle est considérée comme
axes(A, dim)
. Plus flexiblement, vous pouvez utiliser la syntaxe d'expression de fonction anonyme décrite ci-dessous. - Le dernier argument est le corps de la boucle. Ici, c'est ce qui apparaît entre
begin...end
.
Il existe des fonctionnalités supplémentaires de @nloops
décrites dans le reference section.
@nref
suit un modèle similaire, générant A[i_1,i_2,i_3]
à partir de @nref 3 A i
. La pratique générale est de lire de gauche à droite, c'est pourquoi @nloops
est @nloops 3 i A expr
(comme dans for i_2 = axes(A, 2)
, où i_2
est à gauche et la plage est à droite) tandis que @nref
est @nref 3 A i
(comme dans A[i_1,i_2,i_3]
, où le tableau vient en premier).
Si vous développez du code avec Cartesian, vous constaterez peut-être que le débogage est plus facile lorsque vous examinez le code généré, en utilisant @macroexpand
:
julia> @macroexpand @nref 2 A i
:(A[i_1, i_2])
Supplying the number of expressions
Le premier argument de ces deux macros est le nombre d'expressions, qui doit être un entier. Lorsque vous écrivez une fonction que vous souhaitez faire fonctionner dans plusieurs dimensions, ce n'est peut-être pas quelque chose que vous voulez coder en dur. L'approche recommandée est d'utiliser une @generated function
. Voici un exemple :
@generated function mysum(A::Array{T,N}) where {T,N}
quote
s = zero(T)
@nloops $N i A begin
s += @nref $N A i
end
s
end
end
Naturellement, vous pouvez également préparer des expressions ou effectuer des calculs avant le bloc quote
.
Anonymous-function expressions as macro arguments
Peut-être la fonctionnalité la plus puissante de Cartesian
est la capacité de fournir des expressions de fonction anonyme qui sont évaluées au moment de l'analyse. Considérons un exemple simple :
@nexprs 2 j->(i_j = 1)
@nexprs
génère n
expressions qui suivent un modèle. Ce code générerait les déclarations suivantes :
i_1 = 1
i_2 = 1
Dans chaque déclaration générée, un j
"isolé" (la variable de la fonction anonyme) est remplacé par des valeurs dans la plage 1:2
. En général, Cartesian utilise une syntaxe similaire à LaTeX. Cela vous permet de faire des calculs sur l'index j
. Voici un exemple de calcul des pas d'un tableau :
s_1 = 1
@nexprs 3 j->(s_{j+1} = s_j * size(A, j))
générer des expressions
s_1 = 1
s_2 = s_1 * size(A, 1)
s_3 = s_2 * size(A, 2)
s_4 = s_3 * size(A, 3)
Les expressions de fonction anonyme ont de nombreuses utilisations en pratique.
Macro reference
Base.Cartesian.@nloops
— Macro@nloops N itersym rangeexpr bodyexpr
@nloops N itersym rangeexpr preexpr bodyexpr
@nloops N itersym rangeexpr preexpr postexpr bodyexpr
Générer N
boucles imbriquées, en utilisant itersym
comme préfixe pour les variables d'itération. rangeexpr
peut être une expression de fonction anonyme, ou un symbole simple var
dans ce cas la plage est axes(var, d)
pour la dimension d
.
En option, vous pouvez fournir des expressions "pré" et "post". Celles-ci sont exécutées en premier et en dernier, respectivement, dans le corps de chaque boucle. Par exemple :
@nloops 2 i A d -> j_d = min(i_d, 5) begin
s += @nref 2 A j
end
générerait :
for i_2 = axes(A, 2)
j_2 = min(i_2, 5)
for i_1 = axes(A, 1)
j_1 = min(i_1, 5)
s += A[j_1, j_2]
end
end
Si vous souhaitez juste une expression post, fournissez nothing
pour l'expression pré. En utilisant des parenthèses et des points-virgules, vous pouvez fournir des expressions à plusieurs instructions.
Base.Cartesian.@nref
— Macro@nref N A indexexpr
Générer des expressions comme A[i_1, i_2, ...]
. indexexpr
peut être soit un préfixe de symbole d'itération, soit une expression de fonction anonyme.
Exemples
julia> @macroexpand Base.Cartesian.@nref 3 A i
:(A[i_1, i_2, i_3])
Base.Cartesian.@nextract
— Macro@nextract N esym isym
Générer N
variables esym_1
, esym_2
, ..., esym_N
pour extraire des valeurs de isym
. isym
peut être soit un Symbol
, soit une expression de fonction anonyme.
@nextract 2 x y
générerait
x_1 = y[1]
x_2 = y[2]
tandis que @nextract 3 x d->y[2d-1]
donnerait
x_1 = y[1]
x_2 = y[3]
x_3 = y[5]
Base.Cartesian.@nexprs
— Macro@nexprs N expr
Générer N
expressions. expr
doit être une expression de fonction anonyme.
Exemples
julia> @macroexpand Base.Cartesian.@nexprs 4 i -> y[i] = A[i+j]
quote
y[1] = A[1 + j]
y[2] = A[2 + j]
y[3] = A[3 + j]
y[4] = A[4 + j]
end
Base.Cartesian.@ncall
— Macro@ncall N f sym...
Générez une expression d'appel de fonction. sym
représente un nombre quelconque d'arguments de fonction, dont le dernier peut être une expression de fonction anonyme et est développé en N
arguments.
Par exemple, @ncall 3 func a
génère
func(a_1, a_2, a_3)
tandis que @ncall 2 func a b i->c[i]
produit
func(a, b, c[1], c[2])
Base.Cartesian.@ncallkw
— Macro@ncallkw N f kw sym...
Générez une expression d'appel de fonction avec des arguments de mot-clé kw...
. Comme dans le cas de @ncall
, sym
représente un nombre quelconque d'arguments de fonction, dont le dernier peut être une expression de fonction anonyme et est développé en N
arguments.
Exemples
julia> using Base.Cartesian
julia> f(x...; a, b = 1, c = 2, d = 3) = +(x..., a, b, c, d);
julia> x_1, x_2 = (-1, -2); b = 0; kw = (c = 0, d = 0);
julia> @ncallkw 2 f (; a = 0, b, kw...) x
-3
Base.Cartesian.@ntuple
— Macro@ntuple N expr
Génère un N
-uplet. @ntuple 2 i
générerait (i_1, i_2)
, et @ntuple 2 k->k+1
générerait (2,3)
.
Base.Cartesian.@nall
— Macro@nall N expr
Vérifiez si toutes les expressions générées par l'expression de fonction anonyme expr
évaluent à true
.
@nall 3 d->(i_d > 1)
générerait l'expression (i_1 > 1 && i_2 > 1 && i_3 > 1)
. Cela peut être pratique pour vérifier les limites.
Base.Cartesian.@nany
— Macro@nany N expr
Vérifiez si l'une des expressions générées par l'expression de fonction anonyme expr
évalue à true
.
@nany 3 d->(i_d > 1)
générerait l'expression (i_1 > 1 || i_2 > 1 || i_3 > 1)
.
Base.Cartesian.@nif
— Macro@nif N conditionexpr expr
@nif N conditionexpr expr elseexpr
Génère une séquence d'instructions if ... elseif ... else ... end
. Par exemple :
@nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " trop grande")) d->println("Tout va bien")
générerait :
if i_1 > size(A, 1)
error("Dimension ", 1, " trop grande")
elseif i_2 > size(A, 2)
error("Dimension ", 2, " trop grande")
else
println("Tout va bien")
end