Julia宏将`f(dim1,dim2,..)= value`转换为`f(value,dim1,dim2,..)

时间:2017-10-03 13:58:55

标签: 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 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

但是由于for $(loop_wholeElements)部分,上面的宏没有编译并返回语法错误(“无效的迭代规范”)......实际上我不知道如何处理lhs_dims_placeholders和loop_wholeElements中的表达式为了“组装”扩展的表达...

修改

使用d1d2dfix3发布的示例只是一个特定情况,但宏应该能够处理循环的任何维度。 我认为那里的宏确实如此,但我不知道如何构建最终表达..: - (

2 个答案:

答案 0 :(得分:2)

我们可以使用MacroTools.jl作为模板匹配的便捷工具,而不是手动执行那些硬编码的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])

更新:

期望的最终表达是comprehension,似乎由于某种原因,朱莉娅无法弄清楚for expr(其中$expr #=> XXX in XXX)是一种理解。解决方法是直接使用its lowered form

julia> using MacroTools

julia> par1(a, b, c, d) = a + b + c + d
par1 (generic function with 1 method)

julia> par2(a, b) = a + b
par2 (generic function with 1 method)

julia> macro meq(ex)
           @capture(ex, par_(dims__) = rhs_)
           loopElements = []
           dimsPlaceholders = []
           for d in dims
               @capture(d, di_ in DIMi_) || (push!(dimsPlaceholders, d); continue)
               # push!(loopElements, x) 
               push!(loopElements, :($di = $DIMi))
               push!(dimsPlaceholders, di)
           end
           ret = Expr(:comprehension, :($par($(rhs),$(dimsPlaceholders...))), loopElements...)
       end
@meq (macro with 1 method)

julia> prettify(@macroexpand @meq par1!(d1 in DIM1, d2 in DIM2, dfix3) =  par2(d1,d2)+par3(d1,d2))
:($(Expr(:comprehension, :((Main.par1!)(begin 
            (Main.par2)(bee, wildebeest) + (Main.par3)(bee, wildebeest)
        end, bee, wildebeest, Main.dfix3)), :(bee = Main.DIM1), :(wildebeest = Main.DIM2))))

julia> @meq par1(m in 1:2, n in 4:5, 3) =  par2(m,n) + par2(m,n)
2×2 Array{Int64,2}:
 18  21
 21  24

请注意,如果我们使用d1,d2而不是push!(loopElements, x),则生成的表达式中push!(loopElements, :($di = $DIMi))的变量范围将是错误的。让我们等一个知识渊博的人做一个彻底的解释。

答案 1 :(得分:1)

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

return :([$(Expr(:generator,:($(Expr(:call,lhs_par,rhs,lhs_dims_placeholders...))),loop_wholeElements...))])

关键是使用:generator构造函数来创建循环表达式

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

然后生成确切的表达式:

:([(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