2015-12-01 44 views

回答

15

好辦法

推薦的方法做,這是對函數名轉換爲符號,然後查找該符號適當的名稱空間:

julia> fn = "time" 
"time" 

julia> Symbol(fn) 
:time 

julia> getfield(Main, Symbol(fn)) 
time (generic function with 2 methods) 

julia> getfield(Main, Symbol(fn))() 
1.448981716732318e9 

你可以在這裏改變Main任何模塊只看該功能模塊。這使您可以將該組功能限制爲僅可用於該模塊中的功能。您可以使用「裸模塊」來創建僅具有您填充的功能的名稱空間,默認情況下不會從Base導入所有名稱。

糟糕的方法

不推薦但不同的方法很多人似乎達到第一是建立了一串代碼調用該函數,然後解析該字符串,並對其進行評估。例如:

julia> eval(parse("$fn()")) # NOT RECOMMENDED 
1.464877410113412e9 

雖然這很簡單,但不建議,因爲它很慢,很脆弱和危險。解析和評估代碼本質上要複雜得多,因此比在模塊中進行名稱查找要慢 - 名稱查找本質上只是一個哈希表查找。在Julia,代碼是在即時編譯而不是解釋,eval是很多更慢,更昂貴,因爲它不僅涉及解析,而且還生成LLVM代碼,運行優化過程,發射機器代碼,然後最後調用一個函數。解析和評估字符串也很脆弱,因爲在將代碼轉換爲文本時,所有預期含義都會被丟棄。舉個例子,某人無意中提供了一個空的函數名 - 然後的事實,該代碼用於調用函數的語法意外相似性完全喪失:

julia> fn = "" 
"" 

julia> eval(parse("$fn()")) 
() 

哎呀。這不是我們想要的。在這種情況下的行爲是相當無害的,但它可以很容易會更糟糕:

julia> fn = "println(\"rm -rf /important/directory\"); time" 
"println(\"rm -rf /important/directory\"); time" 

julia> eval(parse("$fn()")) 
rm -rf /important/directory 
1.448981974309033e9 

如果用戶的輸入是不可信的,這是一個巨大的安全漏洞。即使你信任用戶,他們仍然有可能無意中提供輸入,這些輸入會做出意想不到的事情。該名稱查找方法避免了這些問題:

julia> getfield(Main, Symbol(fn))() 
ERROR: UndefVarError: println("rm -rf /important/directory"); time not defined 
in eval(::Module, ::Any) at ./boot.jl:225 
in macro expansion at ./REPL.jl:92 [inlined] 
in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:46 

尋找一個名字,然後調用它的功能是明確的,而不是在生成的字符串語法隱含的意圖,所以在最差的一個可獲得有關的錯誤陌生的名字未定義。

性能

如果你要調用一個動態指定函數在內環或一些遞歸計算的一部分,你會希望避免每次調用該函數的時間做一個getfield查找。在這種情況下,您只需要在定義調用它的迭代/遞歸過程之前將const綁定到動態指定的函數。例如:

fn = "deg2rad" # converts angles in degrees to radians 

const f = getfield(Main, Symbol(fn)) 

function fast(n) 
    t = 0.0 
    for i = 1:n 
     t += f(i) 
    end 
    return t 
end 

julia> @time fast(10^6) # once for JIT compilation 
    0.010055 seconds (2.97 k allocations: 142.459 KB) 
8.72665498661791e9 

julia> @time fast(10^6) # now it's fast 
    0.003055 seconds (6 allocations: 192 bytes) 
8.72665498661791e9 

julia> @time fast(10^6) # see? 
    0.002952 seconds (6 allocations: 192 bytes) 
8.72665498661791e9 

結合f必須以獲得最佳性能不變,否則編譯器無法知道你會不會改變f隨時在其他功能點(或甚至東西是不是函數),所以它必須發出在每次循環迭代時動態顯示f的代碼 - 實際上與在循環中手動調用getfield一樣。在這裏,因爲fconst,編譯器知道f不能改變,所以它可以發出快速的代碼,直接調用正確的函數。但是,編譯器有時可以做的比這更好的 - 在這種情況下,它實際上是內聯的deg2rad功能的實現,這僅僅是一個乘以pi/180

julia> @code_llvm fast(100000) 

define double @julia_fast_51089(i64) #0 { 
top: 
    %1 = icmp slt i64 %0, 1 
    br i1 %1, label %L2, label %if.preheader 

if.preheader:          ; preds = %top 
    br label %if 

L2.loopexit:          ; preds = %if 
    br label %L2 

L2:            ; preds = %L2.loopexit, %top 
    %t.0.lcssa = phi double [ 0.000000e+00, %top ], [ %5, %L2.loopexit ] 
    ret double %t.0.lcssa 

if:            ; preds = %if.preheader, %if 
    %t.04 = phi double [ %5, %if ], [ 0.000000e+00, %if.preheader ] 
    %"#temp#.03" = phi i64 [ %2, %if ], [ 1, %if.preheader ] 
    %2 = add i64 %"#temp#.03", 1 
    %3 = sitofp i64 %"#temp#.03" to double 
    %4 = fmul double %3, 0x3F91DF46A2529D39   ; deg2rad(x) = x*(pi/180) 
    %5 = fadd double %t.04, %4 
    %6 = icmp eq i64 %"#temp#.03", %0 
    br i1 %6, label %L2.loopexit, label %if 
} 

如果您需要使用許多不同的做到這一點動態指定功能和您使用的朱0.5(夜間),那麼甚至可以傳遞函數被稱爲在作爲參數:

function fast(f,n) 
    t = 0.0 
    for i = 1:n 
     t += f(i) 
    end 
    return t 
end 

julia> @time fast(getfield(Main, Symbol(fn)), 10^6) 
    0.007483 seconds (1.70 k allocations: 76.670 KB) 
8.72665498661791e9 

julia> @time fast(getfield(Main, Symbol(fn)), 10^6) 
    0.002908 seconds (6 allocations: 192 bytes) 
8.72665498661791e9 

這產生相同的快速代碼如上單參數fast,但會產生每個不同功能的新版本f,你叫它。如果你使用的是Julia 0.4(穩定版),這會起作用,但速度會變慢,不會爲你編譯新版本。如果你想在0.4上達到相同的效果,那麼你必須使用一些元編程來自己生成一個自定義函數。

+3

感謝您的支持。我知道必須有更好的方法。 – Jubobs

相關問題