julia introspection - 获取传递给函数的变量名称

eja*_*ang 6 julia

在朱莉娅,有没有办法获得传递给函数的名称?

x = 10
function myfunc(a)
# do something here
end
assert(myfunc(x) == "x")
Run Code Online (Sandbox Code Playgroud)

我是否需要使用宏或是否有提供内省的本机方法?

小智 8

您可以使用宏获取变量名称:

julia> macro mymacro(arg)
           string(arg)
       end

julia> @mymacro(x)
"x"

julia> @assert(@mymacro(x) == "x")
Run Code Online (Sandbox Code Playgroud)

但正如其他人所说,我不确定你为什么需要这样做.

宏在编译期间在AST(代码树)上运行,并且x作为符号传递到宏:x.您可以将符号转换为字符串,反之亦然.宏用代码替换代码,因此@mymacro(x)只需将其拉出并替换为string(:x).


Isa*_*ton 5

好吧,自相矛盾:从技术上讲,在一个(相当有限的)条件下,这可能以一种非常hacky 的方式实现:函数名称必须只有一个方法签名。这个想法与Python此类问题的答案非常相似。在演示之前,我必须强调,这些是内部编译器细节,可能会发生变化。简要地:

julia> function foo(x)
           bt = backtrace()
           fobj = eval(current_module(), symbol(Profile.lookup(bt[3]).func))
           Base.arg_decl_parts(fobj.env.defs)[2][1][1]
       end
foo (generic function with 1 method)

julia> foo(1)
"x"
Run Code Online (Sandbox Code Playgroud)

让我再次强调这是一个坏主意,不应该用于任何事情!(好吧,回溯显示除外)。这基本上是“愚蠢的编译器技巧”,但我展示它是因为使用这些对象可能具有教育意义,并且解释确实为@ejang 的澄清评论提供了更有用的答案。

解释:

  • bt = backtrace()从当前位置生成...回溯...。bt是一个指针数组,其中每个指针是当前调用堆栈中一个帧的地址。
  • Profile.lookup(bt[3])返回一个LineInfo带有函数名称的对象(以及每个帧的其他几个细节)。请注意,bt[1]bt[2]位于回溯生成函数本身中,因此我们需要进一步向上堆栈以获取调用者。
  • Profile.lookup(...).func返回函数名称(符号:foo
  • eval(current_module(), Profile.lookup(...))返回函数对象与名称关联:foocurrent_module()。如果我们修改function foo为 return的定义fobj,那么请注意foo与 REPL 中的对象等价:

    julia> function foo(x)
               bt = backtrace()
               fobj = eval(current_module(), symbol(Profile.lookup(bt[3]).func))
           end
    foo (generic function with 1 method)
    
    julia> foo(1) == foo
    true
    
    Run Code Online (Sandbox Code Playgroud)
  • fobj.env.defsMethodMethodTablefor foo/返回第一个条目fobj

  • Base.decl_arg_parts是一个辅助函数(在 中定义methodshow.jl),它从给定的 中提取参数信息Method
  • 其余的索引深入到参数的名称。

关于函数只有一个方法签名的限制,原因是多个签名都会defs.nextMethodTable. 据我所知,目前没有公开的接口来获取与给定帧地址关联的特定方法。(作为高级读者的练习:这样做的一种方法是修改地址查找功能jl_getFunctionInfo以同时返回损坏的函数名称,然后可以将其与特定的方法调用重新关联;但是,我不认为我们当前存储了一个从损坏的名称 -> 方法的反向映射)。

还要注意 (1) 回溯很慢 (2) 在 Julia 中没有“函数局部”eval 的概念,所以即使有变量名,我相信实际访问变量(和编译器)是不可能的可能会完全省略局部变量,未使用或以其他方式,将它们放在寄存器中等)

至于评论中提到的IDE风格的自省使用:foo.env.defs如上图所示是“对象自省”的起点。在调试方面,Gallium.jl可以检查给定帧中的 DWARF 局部变量信息。最后,JuliaParser.jl是 Julia 解析器的纯 Julia 实现,在多个 IDE 中积极使用,以在较高级别内省代码块。