Julia ---- 反射机制 Symbol 通过给定的字符串调用函数

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生成一个新版本。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

October-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值