我对元编程不熟悉,所以也许我不理解这一点。我认为@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
。正确的朱利安书写方式是什么?
答案 0 :(得分:3)
请参见supplying the number of expressions:
julia> A = rand(4, 4, 3) # 3D array (Array{Int, 3})
生成的函数有点像宏,因为返回的表达式不返回,而是在调用/调用时编译和执行,它请参见 类型(以及它们的类型参数),例如:
A
是Array{T, N}
,不是数组的值。T
是Int
,而N
是3
!在引用的表达式内部,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
。