用于将 `f(dim1,dim2,..) = value` 转换为 `f(value,dim1,dim2,..)` 的 Julia 宏
Julia macro for transforming `f(dim1,dim2,..) = value` into `f(value,dim1,dim2,..)`
我正在尝试编写一个 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,所以我不确定如何 "assemble" 各个部分的最终表达式..
这是我到目前为止所做的:
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
但是上面的宏没有编译并且 returns 由于 for $(loop_wholeElements)
部分的语法错误(“无效的迭代规范”)......实际上我不知道如何处理表达式在 lhs_dims_placeholders 和 loop_wholeElements 中为了“assemble” 扩展表达式…
编辑:
发布的示例 d1
、d2
和 dfix3
只是一个特定案例,但宏应该能够处理循环的任何维度。
我认为上面的宏可以做到这一点,但我不知道如何构建最终表达式.. :-(
我们可以使用 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
,似乎出于某种原因 Julia 无法弄清楚 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
请注意,如果我们使用push!(loopElements, x)
而不是push!(loopElements, :($di = $DIMi))
,生成表达式中d1,d2
的变量范围将是错误的。还是等有识之士给个详尽的解释吧。
如果你不想为此依赖外部包,我在 Julia discourse 上提供的解决方案也应该有效
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
我正在尝试编写一个 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,所以我不确定如何 "assemble" 各个部分的最终表达式.. 这是我到目前为止所做的:
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
但是上面的宏没有编译并且 returns 由于 for $(loop_wholeElements)
部分的语法错误(“无效的迭代规范”)......实际上我不知道如何处理表达式在 lhs_dims_placeholders 和 loop_wholeElements 中为了“assemble” 扩展表达式…
编辑:
发布的示例 d1
、d2
和 dfix3
只是一个特定案例,但宏应该能够处理循环的任何维度。
我认为上面的宏可以做到这一点,但我不知道如何构建最终表达式.. :-(
我们可以使用 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
,似乎出于某种原因 Julia 无法弄清楚 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
请注意,如果我们使用push!(loopElements, x)
而不是push!(loopElements, :($di = $DIMi))
,生成表达式中d1,d2
的变量范围将是错误的。还是等有识之士给个详尽的解释吧。
如果你不想为此依赖外部包,我在 Julia discourse 上提供的解决方案也应该有效
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