Julia - 在运行时在运行时创建的函数中获取参数
Julia - getting parameters at runtime inside of an at runtime created function
我正在尝试创建一个宏来替换函数体,这样:
@foo function bar(a,b,c)
*my code here*
end
我已经走得很远了,但是我这里的代码需要知道bar(...)
的参数,这是可以做到的
通过使用 Base.@locals
。但问题是 @foo
需要插入它,我认为它使用了错误的 'local scope' 或者在有参数之前被评估。
foo 大概长这样:(expr.args[2]
是函数体)
macro foo(expr::Expr)
...
expr.args[2] = quote
locals = Base.@locals
println(locals)
...
end
:($expr)
end
但是通过这个调用后面创建的函数,只会打印出一个空的字典,而手动创建的函数在使用相同的代码时会打印出参数:
function test(a,b,c)
println(Base.@locals)
end
test(1,2,3) # returns: Dict{Symbol,Any}(:a => 1,:b => 2,:c => 3)
所以我的问题是,我必须如何解决这个问题才能让 Base.@locals
(或类似的方法)给我参数?
而不是依赖 Base.@locals
,我会直接从定义中获取函数参数,然后执行如下操作:
using MacroTools
macro foo(expr)
def = MacroTools.splitdef(expr)
make_pair(arg) = :($(Meta.quot(arg)) => $arg)
def[:body] = quote
d = Dict($(map(make_pair, def[:args])...))
println(d)
$(def[:body])
end
MacroTools.combinedef(def)
end
关于这段代码的几点说明和解释:
- 尝试 rewrite/modify 函数定义的宏可以从使用
MacroTools
中的 splitdef
和 combinedef
函数中受益匪浅。在全球范围内,拟议的宏具有 3 个阶段:
- 将函数定义拆分为单独的部分;
- 在函数体的开头插入你想要的代码;
- re-combine所有部分(包括修改后的主体)变成一个合法的函数定义。
- 包含函数参数的字典由以下关键成分构建:
make_pair
取一个给定的变量名(作为一个符号,比如 :a
),returns 一个计算对 :a => a
;[=43= 的表达式]
- 然后这个
make_pair
函数被 map
传递给来自(分解的)函数定义的所有参数,以构建一个参数对数组;
- 最后这个数组被分配到
Dict
构造函数中。
我们可以检查此示例函数定义是否扩展为预期代码:
julia> Base.remove_linenums!(@macroexpand @foo function bar(a,b,c)
a + b + c
end)
:(function Main.bar(var"#26#a", var"#27#b", var"#28#c"; )
var"#25#d" = Main.Dict(:a => var"#26#a", :b => var"#27#b", :c => var"#28#c")
Main.println(var"#25#d")
begin
var"#26#a" + var"#27#b" + var"#28#c"
end
end)
...更一般地说,一切在运行时也按预期工作:
julia> @foo function bar(a, b, c)
a + b + b
end
bar (generic function with 1 method)
julia> bar(1, 2, 3)
Dict(:a => 1,:b => 2,:c => 3)
5
我正在尝试创建一个宏来替换函数体,这样:
@foo function bar(a,b,c)
*my code here*
end
我已经走得很远了,但是我这里的代码需要知道bar(...)
的参数,这是可以做到的
通过使用 Base.@locals
。但问题是 @foo
需要插入它,我认为它使用了错误的 'local scope' 或者在有参数之前被评估。
foo 大概长这样:(expr.args[2]
是函数体)
macro foo(expr::Expr)
...
expr.args[2] = quote
locals = Base.@locals
println(locals)
...
end
:($expr)
end
但是通过这个调用后面创建的函数,只会打印出一个空的字典,而手动创建的函数在使用相同的代码时会打印出参数:
function test(a,b,c)
println(Base.@locals)
end
test(1,2,3) # returns: Dict{Symbol,Any}(:a => 1,:b => 2,:c => 3)
所以我的问题是,我必须如何解决这个问题才能让 Base.@locals
(或类似的方法)给我参数?
而不是依赖 Base.@locals
,我会直接从定义中获取函数参数,然后执行如下操作:
using MacroTools
macro foo(expr)
def = MacroTools.splitdef(expr)
make_pair(arg) = :($(Meta.quot(arg)) => $arg)
def[:body] = quote
d = Dict($(map(make_pair, def[:args])...))
println(d)
$(def[:body])
end
MacroTools.combinedef(def)
end
关于这段代码的几点说明和解释:
- 尝试 rewrite/modify 函数定义的宏可以从使用
MacroTools
中的splitdef
和combinedef
函数中受益匪浅。在全球范围内,拟议的宏具有 3 个阶段:- 将函数定义拆分为单独的部分;
- 在函数体的开头插入你想要的代码;
- re-combine所有部分(包括修改后的主体)变成一个合法的函数定义。
- 包含函数参数的字典由以下关键成分构建:
make_pair
取一个给定的变量名(作为一个符号,比如:a
),returns 一个计算对:a => a
;[=43= 的表达式]- 然后这个
make_pair
函数被map
传递给来自(分解的)函数定义的所有参数,以构建一个参数对数组; - 最后这个数组被分配到
Dict
构造函数中。
我们可以检查此示例函数定义是否扩展为预期代码:
julia> Base.remove_linenums!(@macroexpand @foo function bar(a,b,c)
a + b + c
end)
:(function Main.bar(var"#26#a", var"#27#b", var"#28#c"; )
var"#25#d" = Main.Dict(:a => var"#26#a", :b => var"#27#b", :c => var"#28#c")
Main.println(var"#25#d")
begin
var"#26#a" + var"#27#b" + var"#28#c"
end
end)
...更一般地说,一切在运行时也按预期工作:
julia> @foo function bar(a, b, c)
a + b + b
end
bar (generic function with 1 method)
julia> bar(1, 2, 3)
Dict(:a => 1,:b => 2,:c => 3)
5