Macros 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 *

我对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 * 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来寻找灵感,如果我找到了解决方案,我会回到过去。再次感谢。