Đây là cách ngây thơ,
all0 <- function(x, FUN)
all(vapply(x, FUN, logical(1)))
và một vòng lặp R ...
all1 <- function(x, FUN) {
for (xi in x)
if (!FUN(xi))
return(FALSE)
TRUE
}
...mà có thể được biên dịch
library(compiler)
all1c <- cmpfun(all1)
... hoặc viết bằng C
library(inline)
allc <- cfunction(signature(x="list", fun="function"), "
SEXP call = PROTECT(lang2(fun, R_NilValue));
int len = Rf_length(x);
for (int i = 0; i < len; ++i) {
SETCADR(call, VECTOR_ELT(x, i));
if (!LOGICAL(eval(call, R_GlobalEnv))[0]) {
UNPROTECT(1);
return Rf_ScalarLogical(FALSE);
}
}
UNPROTECT(1);
return Rf_ScalarLogical(TRUE);")
Chúng ta cần để đo hiệu suất, vì vậy
library(microbenchmark)
Trường hợp xấu nhất sẽ có vẻ là rằng tình trạng này vượt qua
n <- 100000
x0 <- x <- vector("list", n)
microbenchmark(all0(x, is.null), all1(x, is.null), all1c(x, is.null),
allc(x, is.null))
## Unit: milliseconds
## expr min lq median uq max neval
## all0(x, is.null) 47.48038 50.58960 52.34946 54.10116 61.94736 100
## all1(x, is.null) 41.52370 44.40024 45.25135 46.68218 53.22317 100
## all1c(x, is.null) 33.76666 35.03008 35.71738 36.41944 45.37174 100
## allc(x, is.null) 13.95340 14.43153 14.78244 15.94688 19.41072 100
vì vậy chúng tôi chỉ nhanh gấp 2 lần so với phiên bản R đã biên dịch - có một cuộc gọi hàm trên mỗi bài kiểm tra, vì vậy chúng tôi chỉ tiết kiệm cho mỗi vòng lặp. Các trường hợp tốt nhất là khi chúng ta thoát ngay lập tức và rõ ràng cho thấy lợi thế của vòng lặp, nhưng sau đó không phải biên soạn hay mã C giúp chúng ta
x[[1]] <- FALSE
microbenchmark(all0(x, is.null), all1(x, is.null), all1c(x, is.null),
allc(x, is.null))
## Unit: microseconds
## expr min lq median uq max neval
## all0(x, is.null) 45376.760 45772.5020 46108.5795 46655.005 54242.687 100
## all1(x, is.null) 1.566 1.9550 2.6335 12.015 14.177 100
## all1c(x, is.null) 1.367 1.7340 2.0345 9.359 17.438 100
## allc(x, is.null) 1.229 1.6925 4.6955 11.628 23.378 100
Dưới đây là một trường hợp trung gian, mà không thực sự chứa bất kỳ bất ngờ - C vòng lặp nhanh hơn gấp 2 lần so với vòng lặp R được biên dịch, vì vậy sẽ có khoảng 2x nhanh chóng.
x <- x0
x[[length(x)/2]] <- FALSE
microbenchmark(all0(x, is.null), all1(x, is.null), all1c(x, is.null),
allc(x, is.null))
## Unit: milliseconds
## expr min lq median uq max neval
## all0(x, is.null) 46.85690 49.92969 51.045519 52.653137 59.445611 100
## all1(x, is.null) 20.90066 21.92357 22.582636 23.077863 25.974395 100
## all1c(x, is.null) 16.51897 17.44539 17.825551 18.119202 20.535709 100
## allc(x, is.null) 6.98468 7.18392 7.312575 8.290859 9.460558 100
Rõ ràng thử nghiệm cho NULL ở mức C (VECTOR_ELT(x, i) == R_NilValue
) là rất nhanh, do đó, C mã mà so sánh giá trị NULL là khoảng 100x nhanh hơn so với mã R tương ứng. Có vẻ như allNULL có thể là một sự khái quát hóa đáng giá nếu tốc độ là bản chất, nhưng trường hợp cho một mục đích chung C-tất cả dường như không hấp dẫn lắm. Và dĩ nhiên mã C không xử lý các điều kiện NA hoặc lỗi.
Bạn có thể viết điều này trong C++ hoặc RCpp :) – bartektartanus
Bạn có nghĩa là đối với mỗi vấn đề cá nhân, hoặc sẽ có thể là một cách để thực hiện chung 'all_fast' chức năng trong ' Rcpp'? – Jeroen
Không ... Bạn có thể chuyển hàm làm đối số cho hàm RCpp. – bartektartanus