The (non-exported) Cartesian module provides macros that facilitate writing multidimensional algorithms. Most often you can write such algorithms withstraightforward techniques; however, there are a few cases whereBase.Cartesian
is still useful or necessary.
A simple example of usage is:
@nloops 3 i A begin s += @nref 3 A iend
which generates the following code:
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 endend
In general, Cartesian allows you to write generic code that contains repetitive elements, like the nested loops in this example. Other applications include repeated expressions (e.g., loop unwinding) or creating function calls with variable numbers of arguments without using the "splat" construct (i...
).
The (basic) syntax of@nloops
is as follows:
i
, and variablesi_1, i_2, i_3
were generated.axes(A, dim)
. More flexibly, you can use the anonymous-function expression syntax described below.begin...end
.There are some additional features of@nloops
described in thereference section.
@nref
follows a similar pattern, generatingA[i_1,i_2,i_3]
from@nref 3 A i
. The general practice is to read from left to right, which is why@nloops
is@nloops 3 i A expr
(as infor i_2 = axes(A, 2)
, wherei_2
is to the left and the range is to the right) whereas@nref
is@nref 3 A i
(as inA[i_1,i_2,i_3]
, where the array comes first).
If you're developing code with Cartesian, you may find that debugging is easier when you examine the generated code, using@macroexpand
:
julia> @macroexpand @nref 2 A i:(A[i_1, i_2])
The first argument to both of these macros is the number of expressions, which must be an integer. When you're writing a function that you intend to work in multiple dimensions, this may not be something you want to hard-code. The recommended approach is to use a@generated function
. Here's an example:
@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 endend
Naturally, you can also prepare expressions or perform calculations before thequote
block.
Perhaps the single most powerful feature inCartesian
is the ability to supply anonymous-function expressions that get evaluated at parsing time. Let's consider a simple example:
@nexprs 2 j->(i_j = 1)
@nexprs
generatesn
expressions that follow a pattern. This code would generate the following statements:
i_1 = 1i_2 = 1
In each generated statement, an "isolated"j
(the variable of the anonymous function) gets replaced by values in the range1:2
. Generally speaking, Cartesian employs a LaTeX-like syntax. This allows you to do math on the indexj
. Here's an example computing the strides of an array:
s_1 = 1@nexprs 3 j->(s_{j+1} = s_j * size(A, j))
would generate expressions
s_1 = 1s_2 = s_1 * size(A, 1)s_3 = s_2 * size(A, 2)s_4 = s_3 * size(A, 3)
Anonymous-function expressions have many uses in practice.
Base.Cartesian.@nloops
—Macro@nloops N itersym rangeexpr bodyexpr@nloops N itersym rangeexpr preexpr bodyexpr@nloops N itersym rangeexpr preexpr postexpr bodyexpr
GenerateN
nested loops, usingitersym
as the prefix for the iteration variables.rangeexpr
may be an anonymous-function expression, or a simple symbolvar
in which case the range isaxes(var, d)
for dimensiond
.
Optionally, you can provide "pre" and "post" expressions. These get executed first and last, respectively, in the body of each loop. For example:
@nloops 2 i A d -> j_d = min(i_d, 5) begin s += @nref 2 A jend
would generate:
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] endend
If you want just a post-expression, supplynothing
for the pre-expression. Using parentheses and semicolons, you can supply multi-statement expressions.
Base.Cartesian.@nref
—Macro@nref N A indexexpr
Generate expressions likeA[i_1, i_2, ...]
.indexexpr
can either be an iteration-symbol prefix, or an anonymous-function expression.
Examples
julia> @macroexpand Base.Cartesian.@nref 3 A i:(A[i_1, i_2, i_3])
Base.Cartesian.@nextract
—Macro@nextract N esym isym
GenerateN
variablesesym_1
,esym_2
, ...,esym_N
to extract values fromisym
.isym
can be either aSymbol
or anonymous-function expression.
@nextract 2 x y
would generate
x_1 = y[1]x_2 = y[2]
while@nextract 3 x d->y[2d-1]
yields
x_1 = y[1]x_2 = y[3]x_3 = y[5]
Base.Cartesian.@nexprs
—Macro@nexprs N expr
GenerateN
expressions.expr
should be an anonymous-function expression.
Examples
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...
Generate a function call expression.sym
represents any number of function arguments, the last of which may be an anonymous-function expression and is expanded intoN
arguments.
For example,@ncall 3 func a
generates
func(a_1, a_2, a_3)
while@ncall 2 func a b i->c[i]
yields
func(a, b, c[1], c[2])
Base.Cartesian.@ncallkw
—Macro@ncallkw N f kw sym...
Generate a function call expression with keyword argumentskw...
. As in the case of@ncall
,sym
represents any number of function arguments, the last of which may be an anonymous-function expression and is expanded intoN
arguments.
Examples
julia> using Base.Cartesianjulia> 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
Generates anN
-tuple.@ntuple 2 i
would generate(i_1, i_2)
, and@ntuple 2 k->k+1
would generate(2,3)
.
Base.Cartesian.@nall
—Macro@nall N expr
Check whether all of the expressions generated by the anonymous-function expressionexpr
evaluate totrue
.
@nall 3 d->(i_d > 1)
would generate the expression(i_1 > 1 && i_2 > 1 && i_3 > 1)
. This can be convenient for bounds-checking.
Base.Cartesian.@nany
—Macro@nany N expr
Check whether any of the expressions generated by the anonymous-function expressionexpr
evaluate totrue
.
@nany 3 d->(i_d > 1)
would generate the expression(i_1 > 1 || i_2 > 1 || i_3 > 1)
.
Base.Cartesian.@nif
—Macro@nif N conditionexpr expr@nif N conditionexpr expr elseexpr
Generates a sequence ofif ... elseif ... else ... end
statements. For example:
@nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " too big")) d->println("All OK")
would generate:
if i_1 > size(A, 1) error("Dimension ", 1, " too big")elseif i_2 > size(A, 2) error("Dimension ", 2, " too big")else println("All OK")end
Settings
This document was generated withDocumenter.jl version 1.8.0 onWednesday 9 July 2025. Using Julia version 1.11.6.