Julia中的元编程 - 将变量整数转换为变量名

时间:2016-04-16 03:02:39

标签: metaprogramming julia

简短问题:

我有一个整数变量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

更长的解释:

我最近在朱莉娅发现了Base.Cartesian。它有一些方便的技巧来生成用于索引多维数组的虚拟变量。

笛卡儿中的@ntuple宏可以生成从1开始的序列。例如,@ntuple 5 k->i_k生成(i_1,i_2,i_3,i_4,i_5)。在@generated函数内,如果W=5,那么@ntuple ($W) k->i_k将产生相同的序列。这不起作用:@ntuple 1 k->i_(k+$W)

我无法找到一种方法来生产,例如i_3如果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

3 个答案:

答案 0 :(得分:3)

我无法理解你想要什么,但有一些注意事项:

  1. @generated参数是宏体中的 Types 。例如。在@generated function testfunc(N)中,N类似Int64而不是3

  2. 如果要连接变量名,可能需要按Symbol或其他方式构造变量名,而不仅仅是引用。

  3. 例如

    macro testfunc(N)
        Symbol("i_$N")
    end
    
    @testfunc(2) # equivalent to i_2
    

答案 1 :(得分:3)

正如其他答案所暗示的那样,关键是明确构造你想要的符号并将其拼接成生成的表达式。另请注意,您可以使用@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

答案 2 :(得分:2)

  

我有一个整数变量N,并且想编写一个宏来生成一个虚拟变量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的正确答案。

也就是说,它可能仍然不足以满足您的需求,即将宏用于函数内部。在gist中,当@testfunc(N) N = 3生成i_3时,我们想在函数中引用i_3,但如果我们写: -

function foo(N)
    i_3 = 9
    return @testfunc(N)
end

foo(3)会给我们UndefVarError: i_N not defined。不确定是否有任何方法可以纠正它。我可以想到两种可能来解决这个问题,一种是将您的流程编写为全局范围内的脚本,另一种是将整个函数放在引用中。