在朱莉娅,有没有办法获得传递给函数的名称?
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).
好吧,自相矛盾:从技术上讲,在一个(相当有限的)条件下,这可能以一种非常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(...))返回函数对象与名称关联:foo的current_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.defsMethod从MethodTablefor foo/返回第一个条目fobj
Base.decl_arg_parts是一个辅助函数(在 中定义methodshow.jl),它从给定的 中提取参数信息Method。关于函数只有一个方法签名的限制,原因是多个签名都会defs.next在MethodTable. 据我所知,目前没有公开的接口来获取与给定帧地址关联的特定方法。(作为高级读者的练习:这样做的一种方法是修改地址查找功能jl_getFunctionInfo以同时返回损坏的函数名称,然后可以将其与特定的方法调用重新关联;但是,我不认为我们当前存储了一个从损坏的名称 -> 方法的反向映射)。
还要注意 (1) 回溯很慢 (2) 在 Julia 中没有“函数局部”eval 的概念,所以即使有变量名,我相信实际访问变量(和编译器)是不可能的可能会完全省略局部变量,未使用或以其他方式,将它们放在寄存器中等)
至于评论中提到的IDE风格的自省使用:foo.env.defs如上图所示是“对象自省”的起点。在调试方面,Gallium.jl可以检查给定帧中的 DWARF 局部变量信息。最后,JuliaParser.jl是 Julia 解析器的纯 Julia 实现,在多个 IDE 中积极使用,以在较高级别内省代码块。