2012-04-05 33 views
12

Tôi thực hiện rất nhiều việc sử dụng 'do.call' để tạo các cuộc gọi chức năng. Ví dụ:do.call kết hợp với "::"

myfun <- "rnorm"; 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 

Tuy nhiên, đôi khi tôi muốn gọi hàm một cách rõ ràng từ gói nhất định. Tương tự như ví dụ: stats::rnorm(n=10, mean=5). Có cách nào tôi có thể sử dụng do.call hay tạo một hàm hoạt động giống như do.call để làm việc này:

myfun <- "stats::rnorm"; 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 
+1

cách về 'do.call (số liệu thống kê :: rnorm, myargs)'? – kohske

+1

@kohske - sau đó có vẻ như 'stats :: rnorm (n = 10, mean = 5)' thậm chí đơn giản hơn :-) – Tommy

+1

nhưng trong trường hợp đó bạn không thể sử dụng danh sách làm đối số của nó :-( – kohske

Trả lời

18

Không có hàm gọi là "stats :: rnorm". Bạn phải tìm ra rnorm chức năng trong "số liệu thống kê" không gian tên:

myfun <- get("rnorm", asNamespace("stats")) 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 

Bây giờ bạn có thể dĩ nhiên cũng đi từ một cái tên như "số liệu thống kê :: rnorm" và chia nó thành một phần không gian tên và tên hàm:

funname <- "stats::rnorm" 
fn <- strsplit(funname, "::")[[1]] 
myfun <- if (length(fn)==1) fn[[1]] else get(fn[[2]], asNamespace(fn[[1]])) 
myargs <- list(n=10, mean=5); 
do.call(myfun, myargs); 

cập nhật tôi chỉ muốn chứng minh rằng phương pháp này là 2.5x nhanh hơn so với một từ @Jeroen ...

do.call.tommy <- function(what, args, ...) { 
    if(is.character(what)){ 
    fn <- strsplit(what, "::")[[1]] 
    what <- if(length(fn)==1) { 
     get(fn[[1]], envir=parent.frame(), mode="function") 
    } else { 
     get(fn[[2]], envir=asNamespace(fn[[1]]), mode="function") 
    } 
    } 

    do.call(what, as.list(args), ...) 
} 

# Test it 
do.call.tommy(runif, 10) 
f1 <- function(FUN) do.call.tommy(FUN, list(5)) 
f2 <- function() { myfun<-function(x) x; do.call.tommy(myfun, list(5)) } 
f1(runif) 
f1("stats::runif") 
f2() 

# Test the performance...  
system.time(for(i in 1:1e4) do.call.jeroen("stats::runif", list(n=1, max=50))) # 1.07 secs 
system.time(for(i in 1:1e4) do.call.tommy("stats::runif", list(n=1, max=50))) # 0.42 secs 
11

Bạn có thể xóa dấu ngoặc kép: đó sẽ là chính hàm đó, chứ không phải tên của nó.

myfun <- stats::rnorm 
myargs <- list(n=10, mean=5) 
do.call(myfun, myargs) 
0

Cảm ơn bạn đã trả lời. Tôi nghĩ tôi sẽ cho một cái gì đó như thế này:

do.call.jeroen <- function(what, args, ...){ 
    if(is.function(what)){ 
    what <- deparse(as.list(match.call())$what); 
    } 
    myfuncall <- parse(text=what)[[1]]; 
    mycall <- as.call(c(list(myfuncall), args)); 
    eval(mycall, ...); 
} 

Đó có vẻ là một sự tổng quát tốt đẹp của do.call vì vậy mà tôi vẫn có thể vượt qua trên một chuỗi ký tự cho lập luận what, nhưng nó gọn gàng giả lập một cuộc gọi stats::rnorm(n=10, mean=5).

myfun1 <- "rnorm"; 
myfun2 <- "stats::rnorm"; 
myargs <- list(n=10, mean=5); 
do.call.jeroen(myfun1, myargs); 
do.call.jeroen(myfun2, myargs); 
do.call.jeroen(rnorm, myargs); 
do.call.jeroen(stats::rnorm, myargs); 

Một điều thú vị về điều này là nếu hàm tôi đang gọi sử dụng match.call() để lưu trữ cuộc gọi ở đâu đó, nó sẽ giữ nguyên tên hàm thực. Ví dụ:

do.call.jeroen ("số liệu thống kê :: GLM", danh sách (công thức = tốc độ ~ dist, data = as.name ('xe')))

Call: stats::glm(formula = speed ~ dist, data = cars) 

Coefficients: 
(Intercept)   dist 
    8.2839  0.1656 

Degrees of Freedom: 49 Total (i.e. Null); 48 Residual 
Null Deviance:  1370 
Residual Deviance: 478 AIC: 260.8 
+0

Tôi không thực sự yêu thực tế là bạn sử dụng 'deparse',' parse' và 'eval' để giải quyết vấn đề này, vì nó không xử lý' f <-function (x) do.call.jeroen (x, list (n = 10))); f (runif) ', và nó chậm hơn gấp hai lần khi gọi' do.call' ... Tôi đã cập nhật câu trả lời của mình bằng một cách khác. – Tommy

+0

Cảm ơn. Tôi cũng đã cập nhật phản hồi của mình một chút. về 'do.call.jeroen' là tên hàm ban đầu vẫn được giữ nguyên, thay vì thay thế nó bằng một hàm ẩn danh. – Jeroen

Các vấn đề liên quan