Macros Julia宏,用于将`f(dim1,dim2,…)=value`转换为`f(value,dim1,dim2,…)`

Macros Julia宏,用于将`f(dim1,dim2,…)=value`转换为`f(value,dim1,dim2,…)`,macros,julia,metaprogramming,Macros,Julia,Metaprogramming,我正在尝试编写一个Julia宏来转换以下内容: [par1!( par2(d1,d2)+par3(d1,d2) ,d1,d2,dfix3) for d1 in DIM1, d2 in DIM2] (不是很鼓舞人心)变成更具可读性的东西,比如: @meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2) 其中par1!()是一个设置多维数据的函数,par2()是一个getData()类型的函数 我试图用一个宏来实

我正在尝试编写一个Julia宏来转换以下内容:

[par1!( par2(d1,d2)+par3(d1,d2)   ,d1,d2,dfix3) for d1 in DIM1, d2 in DIM2]
(不是很鼓舞人心)变成更具可读性的东西,比如:

@meq par1!(d1 in DIM1, d2 in DIM2, dfix3) =  par2(d1,d2)+par3(d1,d2)
其中
par1!()
是一个设置多维数据的函数,
par2()
是一个getData()类型的函数

我试图用一个宏来实现它,但由于我是第一次接触julia marcro,我不知道如何从各个片段中“组装”最终表达式。。 以下是我迄今为止所做的工作:

macro meq(eq)
   # dump(eq)
    lhs_par               = eq.args[1].args[1]
    rhs                   = eq.args[2]
    lhs_dims              = eq.args[1].args[2:end]
    loop_counters         = [d.args[2] for d in lhs_dims if typeof(d) == Expr]
    loop_sets             = [d.args[3] for d in lhs_dims if typeof(d) == Expr]
    loop_wholeElements    = [d for d in lhs_dims if typeof(d) == Expr]
    lhs_dims_placeholders = []
    for d in lhs_dims
        if typeof(d) == Expr
            push!(lhs_dims_placeholders,d.args[2])
        else
            push!(lhs_dims_placeholders,d)
        end
    end
    outExp =  quote
      [$(lhs_par)($(rhs),$(lhs_dims_placeholders ...)) for  $(loop_wholeElements ...) ]
    end
    #show(outExp)
    return outExp
end
但是,由于$(loop\u wholeElements)部分的
,上面的宏没有编译并返回语法错误(“无效迭代规范”)。事实上,我不知道如何处理lhs\u dims\u占位符和loop\u wholeElements中的表达式,以便“组装”扩展表达式

编辑:

使用
d1
d2
dfix3
发布的示例只是一个特定情况,但宏应该能够处理循环的任何维度。。
我认为上面的宏可以做到这一点,但我不知道如何构建最终表达式..:-(

我们可以将其用作模板匹配的便捷工具,而不是手动执行那些硬编码的
args
匹配工作:

julia> using MacroTools

julia> macro meq(ex)
           @capture(ex, f_(d1_ in dim1_, d2_ in dim2_, dfix3_) = body__)
           ret = :([$f($(body[]), $d1, $d2, $dfix3) for $d1 in $dim1, $d2 in $dim2])
       end
@meq (macro with 1 method)

julia> prettify(@macroexpand @meq par1!(d1 in DIM1, d2 in DIM2, dfix3) =  par2(d1,d2)+par3(d1,d2))
:([(Main.par1!)((Main.par2)(lobster, redpanda) + (Main.par3)(lobster, redpanda), lobster, redpanda, Main.dfix3) for lobster = Main.DIM1, redpanda = Main.DIM2])

更新: 所需的最终表达式是一个
理解
,似乎出于某种原因,Julia无法理解expr的
(其中
$expr#=>XXX中的XXX
)是一个理解。解决方法是直接使用:


请注意,如果使用
push!(loopElements,x)
而不是
push!(loopElements,:($di=$DIMi)),则生成表达式中
d1,d2
的变量范围将是错误的
。让我们等待有知识的人给出详细的解释。

如果您不想依赖外部软件包,我在Julia对话中提供的解决方案也应该有效

return :([$(Expr(:generator,:($(Expr(:call,lhs_par,rhs,lhs_dims_placeholders...))),loop_wholeElements...))])
关键是使用:生成器构造函数生成循环表达式

此外,可以用rhs.args[n]替换rhs,以消除引号块并直接插入表达式

这将生成精确的表达式:

:([(par1!(par2(d1, d2) + par3(d1, d2), d1, d2, dfix3) for d1 in DIM1, d2 in DIM2)])
编辑:

好的,我继续测试这个:

return Expr(:comprehension,Expr(:generator,Expr(:call,lhs_par,rhs.args[2],lhs_dims_placeholders...),loop_wholeElements...))
结束

然后像这样计算结果

meq(:(par1!(d1 = 1:2, d2 = 1:2, 3) =  par2(d1,d2)+par3(d1,d2))) |> eval

谢谢你的帮助!如果我真的不能得到宏I中最终表达式的组成,我将尝试使用MacroTool.jlwrote@Antonello您的宏乍一看应该可以工作,但由于某些我无法解释的原因,它无法工作(请参阅上面的编辑).谢谢..我也这么想,问题是因为理解..我现在没有访问pc的权限,明天我会尝试..否则我总是可以逐段构建“代码字符串”,然后使用parse()生成一个表达式。@Antonello我认为在Julia中使用
string-construction+parse
类方法不是一个好主意,因为我们完全控制ASTs(对Jeff来说是thx)必须有一个正确的方法来实现这一点。只需提到在
循环中直接使用
Expr
x中的x
s将导致范围问题,我们需要将
x中的x
更改为
x=x
来解决这个问题,请看我更新帖子中的示例。谢谢。(a)使用生成器表达式时存在问题,因为理解被一个额外的括号(
[(expr)]
而不是
[expr]
);(b)我需要保持
rhs
,就好像我用
rhs.args[2]替换一样
rhs包含其他理解,它们的循环部分从a中a的
,b中b的
转换为a=a,b=b的
。这是Julia中的错误吗?@Antonello我不确定这是否是错误。我已经发布了MWE。是的,我现在看到我的原始解决方案中有一个额外的括号。这是最后的更正在这个版本中,这应该会提供您所需要的:
returnexpr(:construction,Expr(:generator,Expr(:call,lhs\u par,rhs.args[2],lhs\u dims\u占位符),loop\u wholeElements…)
尽管如此,您在使用
rhs.args[2]时需要小心
如果您不是从REPL输入命令,那么您需要将
[2]
替换为
[1]
。它返回
:([par1!(par2(d1,d2)+par3(d1,d2),Any[:d1,:d2,:dfix3]),用于DIM1中的d1,DIM2中的d2])
meq(:(par1!(d1 = 1:2, d2 = 1:2, 3) =  par2(d1,d2)+par3(d1,d2))) |> eval