从字符串创建函数时的 Julia 作用域问题
Julia scoping issue when creating function from string
我想构建一个 Julia 应用程序,用户可以在其中使用配置文件(因此作为字符串)指定函数。然后需要在程序中评估函数之前解析配置文件。
问题是虽然函数名称在本地是已知的,但在包含解析器的模块中是未知的。我想到的一种解决方案是将本地 eval
函数传递给解析函数,但这似乎不太优雅。
我试图在这里提出一个最小的工作示例,其中函数名称已经包含在字符串中,而不是解析配置文件:
module MyFuns
function myfun(a)
return a+2
end
end
module MyUtil
# in a real application, parseconfig would parse the configuration file to extract funstr
function parseconfig(funstr)
return eval(Meta.parse(funstr))
end
function parseconfig(funstr, myeval)
return myeval(Meta.parse(funstr))
end
end
# test 1 -- succeeds
f1 = MyFuns.myfun
println("test1: $(f1(1))")
# test 2 -- succeeds
f2 = MyUtil.parseconfig("MyFuns.myfun", eval)
println("test2: $(f2(1))")
# test 3 -- fails
f3 = MyUtil.parseconfig("MyFuns.myfun")
println("test3: $(f3(1))")
输出为:
test1: 3
test2: 3
ERROR: LoadError: UndefVarError: MyFuns not defined
那么,第二种方法可行,但是有没有更好的方法来实现目标?
Meta.parse()
会将您的字符串转换为 AST。 MyFuns.myfun
指的是什么取决于您使用的 eval()
提供的范围。
您的示例的问题在于 MyUtil
中的 eval()
将在该模块的上下文中进行计算。如果那是所需的行为,您只是想念在 MyUtil
.
中使用 MyFuns
但你真正想做的是写一个宏。这允许在解析程序时将代码包含在 运行 之前。宏将有权访问一个特殊参数 __module__
,这是使用宏的上下文。所以 __module__.eval()
将在那个范围内执行表达式。
foo = "outside"
module MyMod
foo = "inside"
macro eval(string)
expr = Meta.parse(string)
__module__.eval(expr)
end
end
MyMod.@eval "foo"
# Output is "outside"
另见关于宏的解释:
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#man-macros-1
并且为了将@MauricevanLeeuwen 的答案转换为我的问题的框架,此代码将起作用:
module MyFuns
function myfun(a)
return a+2
end
end
module MyUtil
macro parseconfig(funstr)
__module__.eval(Meta.parse(funstr))
end
end
f4 = MyUtil.@parseconfig "MyFuns.myfun"
println("test4: $(f4(1))")
我想构建一个 Julia 应用程序,用户可以在其中使用配置文件(因此作为字符串)指定函数。然后需要在程序中评估函数之前解析配置文件。
问题是虽然函数名称在本地是已知的,但在包含解析器的模块中是未知的。我想到的一种解决方案是将本地 eval
函数传递给解析函数,但这似乎不太优雅。
我试图在这里提出一个最小的工作示例,其中函数名称已经包含在字符串中,而不是解析配置文件:
module MyFuns
function myfun(a)
return a+2
end
end
module MyUtil
# in a real application, parseconfig would parse the configuration file to extract funstr
function parseconfig(funstr)
return eval(Meta.parse(funstr))
end
function parseconfig(funstr, myeval)
return myeval(Meta.parse(funstr))
end
end
# test 1 -- succeeds
f1 = MyFuns.myfun
println("test1: $(f1(1))")
# test 2 -- succeeds
f2 = MyUtil.parseconfig("MyFuns.myfun", eval)
println("test2: $(f2(1))")
# test 3 -- fails
f3 = MyUtil.parseconfig("MyFuns.myfun")
println("test3: $(f3(1))")
输出为:
test1: 3
test2: 3
ERROR: LoadError: UndefVarError: MyFuns not defined
那么,第二种方法可行,但是有没有更好的方法来实现目标?
Meta.parse()
会将您的字符串转换为 AST。 MyFuns.myfun
指的是什么取决于您使用的 eval()
提供的范围。
您的示例的问题在于 MyUtil
中的 eval()
将在该模块的上下文中进行计算。如果那是所需的行为,您只是想念在 MyUtil
.
MyFuns
但你真正想做的是写一个宏。这允许在解析程序时将代码包含在 运行 之前。宏将有权访问一个特殊参数 __module__
,这是使用宏的上下文。所以 __module__.eval()
将在那个范围内执行表达式。
foo = "outside"
module MyMod
foo = "inside"
macro eval(string)
expr = Meta.parse(string)
__module__.eval(expr)
end
end
MyMod.@eval "foo"
# Output is "outside"
另见关于宏的解释: https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#man-macros-1
并且为了将@MauricevanLeeuwen 的答案转换为我的问题的框架,此代码将起作用:
module MyFuns
function myfun(a)
return a+2
end
end
module MyUtil
macro parseconfig(funstr)
__module__.eval(Meta.parse(funstr))
end
end
f4 = MyUtil.@parseconfig "MyFuns.myfun"
println("test4: $(f4(1))")