fun1,fun2并且fun3似乎按預期作業:
fun1 <- function(fun, x) {
fun(x)
}
fun1(mean, 1:10)
fun1(as.character, 1:10)
fun1(notafun, 1:10)
fun2 <- function(fun, x) {
fun <- match.fun(fun)
fun(x)
}
fun2(mean, 1:10)
fun2(as.character, 1:10)
fun2(notafun, 1:10)
fun3 <- function(fun, x) {
fun <- deparse(substitute(fun))
do.call(fun, list(x))
}
fun3(mean, 1:10)
fun3(as.character, 1:10)
fun3(notafun, 1:10)
一般而言,一種策略是首選嗎?到目前為止,我只注意到match.fun如果fun指定為字串也可以使用。
我的用例是本地使用的包中的非匯出函式(如果我不能指定fun為字串,則不是限制)。使用match.fun而不是“直接”提供函式(如 in fun1)有什么好處。
uj5u.com熱心網友回復:
首先,檔案!以下是來自以下方面的相關部分?match.fun:
當在以函式為引數的函式內部呼叫時,提取所需的函式物件,同時避免與其他型別的物件進行不希望的匹配。
如果
FUN是函式,則回傳。如果它是一個符號(例如,用反引號括起來)或長度為 1 的字符向量,則會get在呼叫者的父級環境中使用它進行查找。
因此,match.fun有兩個主要好處:
- 它為用戶提供了傳遞字串和符號而不是函式的選項。
- 它提供了型別安全性,因為回傳值始終是一個函式。這使您的源代碼不僅更加健壯,而且更加透明:無需閱讀您的檔案
fun2即可知道其引數fun必須指定一個函式。
并且它幾乎不以性能為代價提供這些好處:
x1 <- mean
x2 <- "mean"
x3 <- quote(mean)
microbenchmark::microbenchmark(match.fun(x1), match.fun(x2), match.fun(x3), times = 1000L)
# Unit: nanoseconds
# expr min lq mean median uq max neval
# match.fun(x1) 287 328 362.481 328 328 1681 1000
# match.fun(x2) 1599 1681 1820.892 1681 1763 7544 1000
# match.fun(x3) 1599 1640 1783.049 1681 1722 7339 1000
由于這些原因,match.fun在嘗試評估函式呼叫(如在您的 中fun2)之前進行驗證幾乎總是比等待并希望可以評估呼叫(如在您的fun1和 中fun3)要好。即使您的函式沒有被匯出,即使您從不傳遞字串或符號,該原則仍然適用,因為透明性(參見 2)使您的源代碼更易于閱讀和維護。
您fun3的獨特之處在于它允許用戶傳遞未計算的運算式,但由于多種原因,這種方法存在問題:
- 它不會在其他函式中按預期作業;見@Hong Ooi 的評論/回答。
- 您不能傳遞使用雙冒號或三重冒號運算子訪問的函式,或者匿名函式,或者更一般地說,任何間接評估函式的運算式:
fun3(base::mean, 1:10) # Error in `base::mean`(1:10) : could not find function "base::mean" fun3(function(x) mean(x), 1:10) # Error in `function(x) mean(x)`(1:10) : # could not find function "function(x) mean(x)" fun3(match.fun(mean), 1:10) # Error in `match.fun(mean)`(1:10) : # could not find function "match.fun(mean)" - 即使它確實按您的預期作業,它也主要是霧里看花:如果結果
deparse(substitute(fun))是一個字串,命名一個可從呼叫環境訪問的函式,那么首先就不需要deparse(substitute(fun)),因為fun會對該函式進行評估反正。它無所事事地做額外的作業:microbenchmark::microbenchmark(fun1(mean, 1:10), fun3(mean, 1:10), times = 1000L) # Unit: microseconds # expr min lq mean median uq max neval # fun1(mean, 1:10) 2.009 2.378 2.700055 2.460 2.788 14.350 1000 # fun3(mean, 1:10) 9.020 10.127 10.991813 10.701 11.480 52.398 1000
總而言之,match.fun在您期望函式作為引數時使用它是一種很好的做法。match.fun如果您想接受函式而不是字串或符號,您可能會避免,但在這種情況下,進行測驗仍然是一個好習慣:
function(FUN, ...) {
if (!is.function(FUN)) {
stop("oops")
}
## do stuff
}
uj5u.com熱心網友回復:
一個關鍵的區別是,如果在封閉的函式中呼叫它fun3將會失敗,例如:
g <- function(f, x)
{
fun3(f, x)
}
g(mean, 1:10)
# Error in f(1:10) : could not find function "f"
一般來說,除非絕對必要,否則盡量避免使用非標準的評估技巧。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/368971.html
