Julia 中的元编程——将整数拼接成变量名
Metaprogramming in Julia -- Splice integer into variable name
小问题:
我有一个整数变量 N
,我想编写一个宏来生成一个虚拟变量 i_($N)
。
一次尝试:
@generated function testfunc(N)
:(i_($N))
end
testfunc(5) # Desired behavior i_5
ERROR: UndefVarError: i_ not defined
in testfunc at none:2
更长的解释:
我最近在 Julia 中发现了 Base.Cartesian
。它有一些方便的技巧来生成用于索引多维数组的虚拟变量。
笛卡尔中的@ntuple
宏可以生成从1开始的序列。例如,@ntuple 5 k->i_k
生成(i_1,i_2,i_3,i_4,i_5)
。在@generated 函数中,if W=5
then @ntuple ($W) k->i_k
将产生相同的序列。这不起作用:@ntuple 1 k->i_(k+$W)
我想不出一种方法来生产,例如i_3
if N=3
(这可能在@generated 函数内)。
我的最终目标是在笛卡尔坐标系中使用 @nloops
循环遍历一系列虚拟变量,并将结果存储在由其中一个虚拟变量索引的某个存储向量中,例如类似于:
@nloops ($W) i A begin
# Example generated code for N=2, W=3:
# storage[i_2] *= A[i_1, i_2, i_3]
storage[i_($N)] *= A[(@ntuple ($W) k->i_k)...]
end
我没明白你到底想要什么,但有一些注意事项:
@generated
参数在宏主体中是 Types。例如。在 @generated function testfunc(N)
中,N
类似于 Int64
而不是 3
。
如果你想连接一个变量名,你可能需要通过Symbol
或其他方式构造它,而不仅仅是引用。
例如
macro testfunc(N)
Symbol("i_$N")
end
@testfunc(2) # equivalent to i_2
I have a Integer variable N and would like to write a macro to produce a single dummy variable i_($N).
要直接回答您的问题,您可以通过以下方式进行:-
i_5 = 7
macro testfunc(expr)
return symbol(:i_, expr)
end
@testfunc 5 #This give us back the value of `i_5`, which is 7
@testfunc(5) #Another way to write the same macro.
所以如果我们有一个函数:-
function foo()
i_3 = 9
return @testfunc(3)
end
foo()
会给我们 9
.
的正确答案
也就是说,它可能仍然不足以满足您的需求,即将要在函数内部使用的宏。在要点中,当 @testfunc(N)
where N = 3
生成 i_3
时,我们想在函数中引用 i_3
,但是如果我们写:-
function foo(N)
i_3 = 9
return @testfunc(N)
end
foo(3)
会给我们一个 UndefVarError: i_N not defined
。不确定是否有任何方法可以纠正它。我可以想到 2 种可能性来解决这个问题,一种是将您的过程编写为全局范围内的脚本,另一种是将整个函数放在引号中。
正如其他答案所暗示的那样,关键是显式构造您想要的符号并将其拼接到生成的表达式中。另请注意,您可以使用 @nref
.
简化 A[i_1, i_2, …]
表达式
@nloops ($W) i A begin
storage[$(symbol(:i_, N)] *= @nref $W A i
end
根据我的经验,学习如何使用这些宏的最佳方法是使用 macroexpand
:
julia> using Base.Cartesian
W = 3
N = 2
macroexpand(:(@nloops ($W) i A begin
storage[$(symbol(:i_, N))] *= @nref $W A i
end))
quote # cartesian.jl, line 31:
for i_3 = 1:size(A,3) # cartesian.jl, line 32:
nothing # cartesian.jl, line 33:
begin # cartesian.jl, line 31:
for i_2 = 1:size(A,2) # cartesian.jl, line 32:
nothing # cartesian.jl, line 33:
begin # cartesian.jl, line 31:
for i_1 = 1:size(A,1) # cartesian.jl, line 32:
nothing # cartesian.jl, line 33:
begin # none, line 5:
storage[i_2] *= A[i_1,i_2,i_3]
end # cartesian.jl, line 34:
nothing
end
end # cartesian.jl, line 34:
nothing
end
end # cartesian.jl, line 34:
nothing
end
end
小问题:
我有一个整数变量 N
,我想编写一个宏来生成一个虚拟变量 i_($N)
。
一次尝试:
@generated function testfunc(N)
:(i_($N))
end
testfunc(5) # Desired behavior i_5
ERROR: UndefVarError: i_ not defined
in testfunc at none:2
更长的解释:
我最近在 Julia 中发现了 Base.Cartesian
。它有一些方便的技巧来生成用于索引多维数组的虚拟变量。
笛卡尔中的@ntuple
宏可以生成从1开始的序列。例如,@ntuple 5 k->i_k
生成(i_1,i_2,i_3,i_4,i_5)
。在@generated 函数中,if W=5
then @ntuple ($W) k->i_k
将产生相同的序列。这不起作用:@ntuple 1 k->i_(k+$W)
我想不出一种方法来生产,例如i_3
if N=3
(这可能在@generated 函数内)。
我的最终目标是在笛卡尔坐标系中使用 @nloops
循环遍历一系列虚拟变量,并将结果存储在由其中一个虚拟变量索引的某个存储向量中,例如类似于:
@nloops ($W) i A begin
# Example generated code for N=2, W=3:
# storage[i_2] *= A[i_1, i_2, i_3]
storage[i_($N)] *= A[(@ntuple ($W) k->i_k)...]
end
我没明白你到底想要什么,但有一些注意事项:
@generated
参数在宏主体中是 Types。例如。在@generated function testfunc(N)
中,N
类似于Int64
而不是3
。如果你想连接一个变量名,你可能需要通过
Symbol
或其他方式构造它,而不仅仅是引用。
例如
macro testfunc(N)
Symbol("i_$N")
end
@testfunc(2) # equivalent to i_2
I have a Integer variable N and would like to write a macro to produce a single dummy variable i_($N).
要直接回答您的问题,您可以通过以下方式进行:-
i_5 = 7
macro testfunc(expr)
return symbol(:i_, expr)
end
@testfunc 5 #This give us back the value of `i_5`, which is 7
@testfunc(5) #Another way to write the same macro.
所以如果我们有一个函数:-
function foo()
i_3 = 9
return @testfunc(3)
end
foo()
会给我们 9
.
也就是说,它可能仍然不足以满足您的需求,即将要在函数内部使用的宏。在要点中,当 @testfunc(N)
where N = 3
生成 i_3
时,我们想在函数中引用 i_3
,但是如果我们写:-
function foo(N)
i_3 = 9
return @testfunc(N)
end
foo(3)
会给我们一个 UndefVarError: i_N not defined
。不确定是否有任何方法可以纠正它。我可以想到 2 种可能性来解决这个问题,一种是将您的过程编写为全局范围内的脚本,另一种是将整个函数放在引号中。
正如其他答案所暗示的那样,关键是显式构造您想要的符号并将其拼接到生成的表达式中。另请注意,您可以使用 @nref
.
A[i_1, i_2, …]
表达式
@nloops ($W) i A begin
storage[$(symbol(:i_, N)] *= @nref $W A i
end
根据我的经验,学习如何使用这些宏的最佳方法是使用 macroexpand
:
julia> using Base.Cartesian
W = 3
N = 2
macroexpand(:(@nloops ($W) i A begin
storage[$(symbol(:i_, N))] *= @nref $W A i
end))
quote # cartesian.jl, line 31:
for i_3 = 1:size(A,3) # cartesian.jl, line 32:
nothing # cartesian.jl, line 33:
begin # cartesian.jl, line 31:
for i_2 = 1:size(A,2) # cartesian.jl, line 32:
nothing # cartesian.jl, line 33:
begin # cartesian.jl, line 31:
for i_1 = 1:size(A,1) # cartesian.jl, line 32:
nothing # cartesian.jl, line 33:
begin # none, line 5:
storage[i_2] *= A[i_1,i_2,i_3]
end # cartesian.jl, line 34:
nothing
end
end # cartesian.jl, line 34:
nothing
end
end # cartesian.jl, line 34:
nothing
end
end