julia元编程和nloop变量评估

时间:2018-10-29 00:28:58

标签: macros julia metaprogramming

我对元编程不熟悉,所以也许我不理解这一点。我认为@nloops中的Base.Cartesian宏的目的是在先验未知维度的情况下,可以编写任意数量的嵌套for循环。在该模块的文档中,给出了以下示例:

@nloops 3 i A begin
    s += @nref 3 A i
end

计算结果为

for i_3 = 1:size(A,3)
    for i_2 = 1:size(A,2)
        for i_1 = 1:size(A,1)
            s += A[i_1,i_2,i_3]
        end
    end
end

在这里,数字3是先验的。但是,出于我的目的以及出于我认为已创建nloops的目的,嵌套级别的数量无法提前知道。因此,我将无法对整数3进行硬编码。即使在文档中也指出:

  

@nloops的(基本)语法如下:

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

...

如果我为某个变量分配一个整数值(例如,传递给函数的数组的维数),则nloops宏将不再起作用:

b = 3
@nloops b i A begin
    s += @nref b A i
end

这将返回错误:

ERROR: LoadError: MethodError: no method matching _nloops(::Symbol, ::Symbol, ::Symbol, ::Expr)
Closest candidates are:
  _nloops(::Int64, ::Symbol, ::Symbol, ::Expr...) at cartesian.jl:43
...

我不知道如何让nloop将b变量评估为整数而不是符号。我查看了文档,并尝试了eval以及其他函数和宏的各种迭代,但是它要么被解释为符号,要么被解释为Expr。正确的朱利安书写方式是什么?

1 个答案:

答案 0 :(得分:3)

请参见supplying the number of expressions

julia> A = rand(4, 4, 3)  # 3D array (Array{Int, 3})

生成的函数有点像宏,因为返回的表达式不返回,而是在调用/调用时编译和执行,它请参见 类型(以及它们的类型参数),例如:

  • 在生成的函数中,AArray{T, N}不是数组的值。
  • 所以TInt,而N3

在引用的表达式内部,N被插入到表达式中,语法为$N,其结果为3

julia> @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
mysum (generic function with 1 method)

julia> mysum(A)
23.2791638775186

您可以构造表达式,然后对其求值,即:

julia> s = 0; n = 3;

julia> _3loops = quote
           @nloops $n i A begin
               global s += @nref $n A i
           end
       end
quote
    @nloops 3 i A begin
        global s += @nref(3, A, i)
    end
end

julia> eval(_3loops)

julia> s
23.2791638775186

为了便于阅读,我从AST手动清理了LineNumberNode(还有MacroTools.prettify,它可以帮您完成此工作)。

在REPL中运行此示例需要,以在Julia 1.0的循环内将s声明为 global