1、正确的方式
建议的方法是将函数名转换为符号,然后在适当的命名空间中查找该符号:
fn = "time"
Symbol(fn)
#:time
getfield(Main, Symbol(fn))
#time (generic function with 2 methods)
getfield(Main, Symbol(fn))()
#1.580791892769e9 ,相当于调用 time() 函数
您可以将这里的Main更改为任何模块,以便只查看该模块中的函数。这允许您将可用的函数集限制为仅适用于该模块中可用的函数集。您可以使用“裸模块”创建一个只包含你用到函数的名称空间,而无需从Base导入所有的默认命名空间。
2、错误的方式
虽然不建议使用,但是许多人似乎还是会使用的另一种方法是为调用函数的代码构造一个字符串,然后解析该字符串并对其求值。例如:
julia> eval(parse("$fn()")) # NOT RECOMMENDED 1.464877410113412e9
在 Julia 1.0版本后,这种不安全的使用方式已经被禁止了,会报异常。
3、性能分析
如果要在内部循环中动态调用指定的函数,或者将其作为递归计算的一部分,那么每次调用该函数时都要避免执行查找getfield。在这种情况下,您只需先对其进行const绑定,然后才能再迭代/递归过程中使用该指定函数。例如:
#将角度(度)转换为弧度函数
fn = "deg2rad" # converts angles in degrees to radians
#先用const 定义
const f = getfield(Main, Symbol(fn))
#然后递归中使用
function fast(n)
t = 0.0
for i = 1:n
t += f(i)
end
return t
end
@time fast(10^6) # 第一次使用会执行JIT编译
@time fast(10^6) # 第二次会更快
为了获得最佳性能,必须使用常量绑定f,否则编译器无法知道你是不是会在其他时候更改f指向其他函数(甚至不是函数的某个函数),因为编译器必须在循环 中 的每次循环迭代时动态查找f的代码。在这里,由于f是const,编译器知道f不能更改,所以它可以快速找到代码,直接调用正确的函数。编译器有时甚至可以做得更好——在这种情况下,它实际上是内部关联了deg2rad函数的实现
#可以看到代码的执行情况
@code_llvm fast(100000)
#下面的内容,跟Julia的版本有关,不同的版本 打印出的内容有差异
; @ D:\Julia\StringToFunction:34 within `fast'
; Function Attrs: uwtable
define double @julia_fast_13725(i64) #0 {
top:
; @ D:\Julia\StringToFunction:35 within `fast'
; ┌ @ range.jl:5 within `Colon'
; │┌ @ range.jl:275 within `Type'
; ││┌ @ range.jl:280 within `unitrange_last'
; │││┌ @ operators.jl:341 within `>='
; ││││┌ @ int.jl:424 within `<='
%1 = icmp sgt i64 %0, 0
; └└└└└
br i1 %1, label %L7.L12_crit_edge, label %L29
L7.L12_crit_edge: ; preds = %top
br label %L12
L12: ; preds = %L12, %L7.L12_crit_edge
%value_phi3 = phi double [ 0.000000e+00, %L7.L12_crit_edge ], [ %4, %L12 ]
%value_phi4 = phi i64 [ 1, %L7.L12_crit_edge ], [ %6, %L12 ]
; @ D:\Julia\StringToFunction:36 within `fast'
; │┌ @ int.jl:53 within `+'
%6 = add nuw i64 %value_phi4, 1
; └└
br i1 %5, label %L29, label %L12
L29: ; preds = %L12, %top
%value_phi9 = phi double [ 0.000000e+00, %top ], [ %4, %L12 ]
; @ D:\Julia\StringToFunction:38 within `fast'
ret double %value_phi9
}
如果需要在多个地方动态指定函数,并且使用的是Julia 0.5以上版本,则可以将要调用的函数作为参数传递:
function fast(f,n)
t = 0.0
for i = 1:n
t += f(i)
end
return t
end
@time fast(getfield(Main, Symbol(fn)), 10^6)
# 0.001661 seconds (6 allocations: 192 bytes)
@time fast(getfield(Main, Symbol(fn)), 10^6)
#0.001246 seconds (6 allocations: 192 bytes)
这将生成与上面的单参数fast相同的fast代码,但将为调用它的每个不同函数f生成一个新版本。