Macros Julia宏能否用于根据特定函数实现生成代码?
我对Julia是个新手,我正在学习元编程 我想写一个宏,它接收一个函数的输入,并根据输入的实现细节返回另一个函数 例如:Macros Julia宏能否用于根据特定函数实现生成代码?,macros,julia,metaprogramming,abstract-syntax-tree,Macros,Julia,Metaprogramming,Abstract Syntax Tree,我对Julia是个新手,我正在学习元编程 我想写一个宏,它接收一个函数的输入,并根据输入的实现细节返回另一个函数 例如: function f(x) x + 100 end function g(x) f(x)*x end function h(x) g(x)-0.5*f(x) end 我想编写一个宏,返回如下内容: function h_traced(x) f = x + 100 println("loc 1 x: ", x) g = f *
function f(x)
x + 100
end
function g(x)
f(x)*x
end
function h(x)
g(x)-0.5*f(x)
end
我想编写一个宏,返回如下内容:
function h_traced(x)
f = x + 100
println("loc 1 x: ", x)
g = f * x
println("loc 2 x: ", x)
res = g - 0.5 * f
println("loc 3 x: ", x)
现在,code_降低和code_键入似乎都以CodeInfo的形式返回AST,但是当我尝试在宏中以编程方式使用它时,我得到的是空对象
macro myExpand(f)
body = code_lowered(f)
println("myExpand Body lenght: ",length(body))
end
这样叫
@myExpand :(h)
但是,宏外部的相同调用可以正常工作
code_lowered(h)
最后,即使下面的代码返回一个空的CodeInfo
macro myExpand(f)
body = code_lowered(Symbol("h"))
println("myExpand Body lenght: ",length(body))
end
这可能是难以置信的琐碎,但我无法理解为什么h符号不能解析为定义的函数。我是否遗漏了符号范围的某些内容?我发现将宏视为将输入语法转换为输出语法的一种方法很有用 因此,您可以很好地定义一个宏
@my_macro
,以便
@my_macro function h(x)
g(x)-0.5*f(x)
end
会扩展到
function h_traced(x)
println("entering function: x=", x)
g(x)-0.5*f(x)
end
但是对于这样一个宏,h
仅仅是一个名称,一个可以转换成h
的标识符(技术上是符号)h
不是绑定到此名称的函数(与x=2
涉及将名称x
绑定到整数值2
的方式相同,x
不是2
;x
只是一个可用于引用2
的名称)。与此相反,当调用code\u lowered(h)
时,首先计算h
,并将code\u lowered
作为参数传递其值(这是一个函数)
回到我们的宏:扩展到一个包含g
和f
定义的表达式不仅仅是语法转换:我们离开了纯语法域,因为这样的转换需要“理解”这些是函数,查找它们的定义等等
你认为code\u
和朋友是对的:这是你想要实现的足够的抽象层次。您可能应该研究像或这样的工具。也就是说,如果你对Julia还比较陌生,那么在深入研究这些主题之前,你可能需要更多地熟悉这门语言。你不需要宏,你需要一个宏。它们不仅可以返回代码(Expr
),还可以返回IR(降低的代码)。通常,对于这类事情,人们使用,而不是code\u
。CASE和IRTools都以不同的方式为您简化了实现
基本思想是:
生成一个接受函数及其参数的函数
在该函数中,获取该函数的IR,并根据您的目的对其进行修改
从生成的函数返回新的IR。然后对原始参数进行编译和调用
使用IRTools的简短演示:
julia> IRTools.@dynamo function traced(args...)
ir = IRTools.IR(args...)
p = IRTools.Pipe(ir)
for (v, stmt) in p
IRTools.insertafter!(p, v, IRTools.xcall(println, "loc $v"))
end
return IRTools.finish(p)
end
julia> function h(x)
sin(x)-0.5*cos(x)
end
h (generic function with 1 method)
julia> @code_ir traced(h, 1)
1: (%1, %2)
%3 = Base.getfield(%2, 1)
%4 = Base.getfield(%2, 2)
%5 = Main.sin(%4)
%6 = (println)("loc %3")
%7 = Main.cos(%4)
%8 = (println)("loc %4")
%9 = 0.5 * %7
%10 = (println)("loc %5")
%11 = %5 - %9
%12 = (println)("loc %6")
return %11
julia> traced(h, 1)
loc %3
loc %4
loc %5
loc %6
0.5713198318738266
剩下的留作练习。变量的数量是关闭的,因为它们在转换过程中当然会移动。您必须为此添加一些簿记,或者以某种方式使用Pipe
上的substitute
功能(但我从未完全理解它)。如果需要变量的名称,可以使用IR
构造函数的不同方法获得保留插槽的IR
(现在是广告:我已经写了。目前效率很低,但你可能会从中得到一些想法。)感谢弗朗索瓦的回答。我发现信息丰富,并符合我的观察结果。我会看一看卡带和IRTools来寻找灵感,如果我找到了解决方案,我会回到过去。再次感谢。