我有两个函数
"This is my docstring for `add_1(x)`"
add_1(x) = x + 1
"This is my docstring for `add_2(x)`"
add_2(x) = x + 2
这两个函数的代码非常相似,在Julia中一定有一种聪明的方式可以自动创建它们。
附属问题:如果这些函数是通过程序生成的,那么错误会如何处理?通常堆栈跟踪会显示来自原始文件的行号,但是在这里,几个函数都是从同一行生成的。是否总是显示相同的行号呢?
我有两个函数
"This is my docstring for `add_1(x)`"
add_1(x) = x + 1
"This is my docstring for `add_2(x)`"
add_2(x) = x + 2
这两个函数的代码非常相似,在Julia中一定有一种聪明的方式可以自动创建它们。
附属问题:如果这些函数是通过程序生成的,那么错误会如何处理?通常堆栈跟踪会显示来自原始文件的行号,但是在这里,几个函数都是从同一行生成的。是否总是显示相同的行号呢?
一种解决方案是这样的
["""
"This is my docstring for \`add_$i(x)\`"
add_$i(x) = x + $i
""" for i ∈ 1:3] .|> Meta.parse .|> eval
关于行号,我卡住了。这是故意引发错误后得到的结果:
julia> add_3("arstoen")
ERROR: MethodError: no method matching +(::String, ::Int64)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
+(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:87
+(::LinearAlgebra.UniformScaling, ::Number) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/uniformscaling.jl:145
...
Stacktrace:
[1] add_3(x::String)
@ Main ./none:2
[2] top-level scope
@ REPL[8]:1
还有其他人知道如何使堆栈跟踪更加合理吗?
您需要手动构建代码的抽象语法树(AST),并注入适当的 LineNumberNode
。
让我们从一些AST开始。通常最容易让Julia在开始时生成初始AST,然后进行变异。
code = quote
add_1(x) = x+1
end
尝试运行dump(code)
以查看AST(我在此省略输出,因为输出很长)。
现在我们可以改变它来生成add_2
。您可以将下面的代码放入函数中,并生成您想要的许多变体add_n
,但为了可读性,我只关注add_2
。
code.args[1] = LineNumberNode(2, Symbol("My line of add_2"))
code.args[2].args[1] = :(add_2(x))
code.args[2].args[2].args[1] = LineNumberNode(2, Symbol("My function body of add two"))
code.args[2].args[2].args[2].args[3]=2
julia> eval(code)
add_2 (generic function with 1 method)
julia> add_2(4)
6
julia> add_2("will errror")
ERROR: MethodError: no method matching +(::String, ::Int64)
[...]
Stacktrace:
[1] add_2(x::String)
@ Main .\My function body of add two:2
[2] top-level scope
@ REPL[22]:1
for i in 1:3; fn = Symbol(:add_, i); @eval $fn(x) = x + $i; end
, 这应该可以得到行号。 - mcabbott