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
endqui 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
endEn 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_3ont é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
endNaturellement, 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 = 1Dans 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 bodyexprGé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
endgé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
endSi 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 indexexprGé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 isymGé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 exprGé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]
endBase.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 exprGé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 exprVé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 exprVé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 elseexprGé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