基地。笛卡尔
(非导出的)笛卡尔模块提供了有助于编写多维算法的宏。 大多数情况下,你可以用https://julialang.org/blog/2016/02/iteration[直截了当的技巧];然而,在一些情况下, 基地。笛卡尔 仍然是有用的或必要的。
使用原则
一个简单的用法示例是:
@nloops 3 i A begin
s += @nref 3 A i
end
它生成以下代码:
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
通常,笛卡尔允许您编写包含重复元素的通用代码,如本示例中的嵌套循环。 其他应用包括重复表达式(例如,循环展开)或创建具有可变数量参数的函数调用,而不使用"splat"构造(我。..).
基本语法
的(基本)语法 @nloops 如下所示:
*第一个参数必须是指定循环数的整数(_not_a变量)。
*第二个参数是用于迭代器变量的符号前缀。 在这里,我们使用 i,和变量 i_1,i_2,i_3 产生的。
*第三个参数指定每个迭代器变量的范围。 如果你在这里使用一个变量(符号),它被视为 轴(a,暗淡). 更灵活地,您可以使用下面描述的匿名函数表达式语法。
*最后一个参数是循环的主体。 在这里,这就是出现在 开始。..结束.
还有一些额外的功能 @nloops 描述于 参考部分。
@nref 遵循类似的模式,生成 A[i_1,i_2,i_3] 从 @nref3A i. 一般的做法是从左到右阅读,这就是为什么 @nloops 是 @nloops3我一个expr (如在 对于i_2=轴(A,2),在哪里 i_2 是向左,范围是向右),而 @nref 是 @nref3A i (如在 A[i_1,i_2,i_3],其中阵列首先)。
如果您正在使用笛卡尔语言开发代码,您可能会发现在检查生成的代码时,调试更容易,使用 @macroexpand:
julia> @macroexpand @nref 2 A i
:(A[i_1, i_2])
提供表达式的数量
这两个宏的第一个参数是表达式的数量,它必须是一个整数。 当你正在编写一个你打算在多个维度上工作的函数时,这可能不是你想要硬编码的东西。 推荐的方法是使用 @生成函数. 这里有一个例子:
@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
当然,您也可以在 报价 块。
匿名函数表达式作为宏参数
也许是最强大的功能 笛卡尔 是提供在解析时得到评估的匿名函数表达式的能力。 让我们考虑一个简单的例子:
@nexprs 2 j->(i_j = 1)
@nexprs 生成 n 遵循模式的表达式。 此代码将生成以下语句:
i_1 = 1
i_2 = 1
在每个生成的语句中,一个"孤立" j (匿名函数的变量)被范围内的值替换 1:2. 一般来说,笛卡尔使用类似LaTeX的语法。 这使您可以在索引上进行数学运算 j. 下面是一个计算数组步进的例子:
s_1 = 1
@nexprs 3 j->(s_{j+1} = s_j * size(A, j))
会生成表达式
s_1 = 1
s_2 = s_1 * size(A, 1)
s_3 = s_2 * size(A, 2)
s_4 = s_3 * size(A, 3)
匿名函数表达式在实践中有许多用途。
宏观参考
# *`基地。笛卡尔。@nloops`*-马科罗_
@nloops N itersym rangeexpr bodyexpr
@nloops N itersym rangeexpr preexpr bodyexpr
@nloops N itersym rangeexpr preexpr postexpr bodyexpr
生成 N 嵌套循环,使用 迭代,迭代 作为迭代变量的前缀。 rangeexpr 可以是匿名函数表达式,也可以是简单的符号 瓦尔 在这种情况下,范围是 轴(var,d) 对于尺寸 d.
或者,您可以提供"pre"和"post"表达式。 这些分别在每个循环的主体中首先执行和最后执行。 例如:
@nloops 2 i A d -> j_d = min(i_d, 5) begin
s += @nref 2 A j
end
会产生:
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
如果你只想要一个后表达式,请提供 什么都没有为预表达。 使用括号和分号,可以提供多语句表达式。
# *`基地。笛卡尔。@nref`*-马科罗_
@nref N A indexexpr
生成表达式,如 A[i_1,i_2,。..]. 索引 可以是迭代符号前缀,也可以是匿名函数表达式。
*例子*
julia> @macroexpand Base.Cartesian.@nref 3 A i
:(A[i_1, i_2, i_3])
# *`基地。笛卡尔。@下一个合同`*-马科罗_
@nextract N esym isym
生成 N 变量 esym_1, esym_2, …, esym_N 从 isym,isym. isym,isym 可以是一个 符号 或匿名函数表达式。
@nextract2x y 会产生
x_1 = y[1]
x_2 = y[2]
而 @nextract3x d->y[2d-1] 产量
x_1 = y[1]
x_2 = y[3]
x_3 = y[5]
# *`基地。笛卡尔。@nexprs`*-马科罗_
@nexprs N expr
生成 N 表情。 expr 应该是一个匿名函数表达式。
*例子*
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
# *`基地。笛卡尔。@ncall`*-马科罗_
@ncall N f sym...
生成函数调用表达式。 西姆 表示任意数量的函数参数,其中最后一个可能是匿名函数表达式,并展开为 N 争论。
例如, @ncall3func a 生成
func(a_1, a_2, a_3)
而 @ncall2func a b i->c[i] 产量
func(a, b, c[1], c[2])
# *`基地。笛卡尔。@ncallkw`*-马科罗_
@ncallkw N f kw sym...
生成带有关键字参数的函数调用表达式 千瓦。... 如在 @ncall, 西姆 表示任意数量的函数参数,最后一个参数可能是匿名函数表达式,并展开为 N 争论。
*例子*
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
# *`基地。笛卡尔。@ntuple`*-马科罗_
@ntuple N expr
生成一个 N-元组。 @ntuple2我 会产生 (i_1,i_2),而 @ntuple2k->k+1 会产生 (2,3).
# *`基地。笛卡尔。@nall`*-马科罗_
@nall N expr
检查是否所有由匿名函数表达式生成的表达式 expr 评估至 真的.
@nall3d->(i_d>1) 会生成表达式 (i_1>1&&i_2>1&&i_3>1). 这可以方便边界检查。
# *`基地。笛卡尔。@纳尼`*-马科罗_
@nany N expr
检查anonymous-function表达式生成的任何表达式 expr 评估至 真的.
@nany3d->(i_d>1) 会生成表达式 (i_1>1//i_2>1|/i_3>1).
# *`基地。笛卡尔。@nif`*-马科罗_
@nif N conditionexpr expr
@nif N conditionexpr expr elseexpr
生成的序列 如果。.. 其他人。.. 别的。.. 结束 发言。 例如:
@nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " too big")) d->println("All OK")
会产生:
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