Engee 文档

基地。笛卡尔

笛卡尔(不可导出)模块提供了简化多维算法编写的宏。 大多数情况下,这样的算法可以使用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

一般来说,笛卡尔模块允许您编写包含重复元素的通用代码,例如本例中的嵌套循环。 其他用途包括重复表达式(例如,展开循环)或使用可变数量的参数创建函数调用,而不使用"星形"构造(i。..).

基本语法

宏'@nloops’的(基本)语法如下所示:

  • 第一个参数必须是指定循环次数的整数(而不是变量)。

  • 第二个参数是用于迭代器变量的前缀符号。 这里使用了’i`,生成了变量’i_1,i_2,i_3'。

  • 第三个参数指定每个迭代器变量的范围。 如果您在这里使用变量(符号),它将被视为"轴(a,dim)"。 更灵活地,可以使用下面描述的用于表达匿名函数的语法。

  • 最后一个参数是循环的主体。 这里出现在`begin之间。..结束'。

"@Nloops"宏的其他功能在 在帮助部分

'@nref’遵循类似的模式,从`@nref3A i`生成`A[i_1,i_2,i_3]'。 它通常是从左到右读取的,所以`@nloops`是`@nloops3i a expr`(如`for i_2=axes(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'。 下面是一个例子:

@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

当然,您也可以在’quote`块之前准备表达式或执行计算。

匿名函数表达式作为宏参数

也许笛卡尔最强大的功能是能够为分析期间评估的匿名函数提供表达式。 让我们举一个简单的例子。

@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 N itersym rangeexpr bodyexpr
@nloops N itersym rangeexpr preexpr bodyexpr
@nloops N itersym rangeexpr preexpr postexpr bodyexpr

使用`itersym`作为迭代变量的前缀生成`N`嵌套循环。 'rangeexpr’可以是匿名函数的表达式或简单符号’var`,那么范围具有用于测量`d`的形式`axes(var,d)'。

如有必要,可以指定"前表达式"和"后表达式"。 在每个周期的主体中,它们分别首先和最后执行。 例如:

@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 N A indexexpr

生成表达式,如`A[i_1,i_2,...]`. 'indexexpr’可以是迭代符号的前缀,也可以是匿名函数的表达式。

例子

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’可以是`符号',也可以是匿名函数的表达式。

`@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 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 N f sym...

生成函数调用表达式。 "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 N f kw sym...

生成具有命名参数'kw的函数调用表达式。... 如在 @ncall`,'sym’表示任意数量的函数参数,其中最后一个可以是匿名函数的表达式,并展开为’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 N expr

生成一个`N`元素的元组。 '@ntuple2i’将生成'(i_1,i_2),并且'+@ntuple2k→k+1+ — (2,3).

@nall N expr

检查匿名函数’expr’的表达式生成的任何表达式是否获取值’true'。

+@nall3d→(i_d>1)+`生成表达式(i_1>1&&i_2>1&&i_3>1)'。 这对于检查边界非常有用。

@nany N expr

检查匿名函数’expr’的表达式生成的任何表达式是否获取值’true'。

+@nany3d→(i_d>1)+`生成表达式(i_1>1||i_2>1||i_3>1)'。

@nif N conditionexpr expr
@nif N conditionexpr expr elseexpr

生成运算符的+if序列。.. 其他人。.. 别的。.. 结束+'。 例如:

@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