Как программно создать функцию R?
Недавно Хэдли Уикхэм задал интересный вопрос о списке рассылки r-devel и не смог найти существующий вопрос по теме в StackOverflow, я подумал, что это может быть полезно и для него.
Перефразируем:
Функция R состоит из трех элементов: списка аргументов, тела и среды. Можем ли мы построить функцию программно из этих трех элементов?
- Как вернуть строковое значение из функции Bash
- jquery - разница между $ .functionName и $ .fn.FunctionName
- Определить имена таблиц и столбцов в качестве аргументов в функции plpgsql?
- Как вы удаляете устройство из любого числа в SASS?
- Локальная функция vs Lambda C # 7.0
(Достаточно исчерпывающий ответ достигается в конце streamа в ссылке r-devel выше. Я оставлю это открытым для других, чтобы воссоздать бенчмаркинг различных решений и предоставить его в качестве ответа, но не забудьте привести Хэдли если вы это сделаете. Если никто не встанет через несколько часов, я сделаю это сам.)
- Процедура с аргументом фиктивного аргумента предполагаемой формы должна иметь явный интерфейс
- Как создать пользовательскую функцию EL для вызова статического метода?
- C sizeof пройденного массива
- Что такое «статическая» функция?
- «Срок службы» строкового литерала в C
- Как реализована функция квадратного корня?
- как тестировать облачные функции для Firebase локально на ПК
- Почему в параметрах C ++ последнее значение должно быть добавлено последними?
Это расширение на обсуждение здесь .
Наши три части должны быть списком аргументов, телом и средой.
Для среды мы просто используем env = parent.frame()
по умолчанию.
Нам не нужен обычный старый список аргументов, поэтому вместо этого мы используем alist
который имеет другое поведение:
«… значения не оцениваются, а помеченные аргументы без значения допускаются»
args <- alist(a = 1, b = 2)
Для тела мы приводим наше выражение, чтобы call
:
body <- quote(a + b)
Один из вариантов - преобразовать args
в список пар, а затем просто вызвать функцию function
используя eval
:
make_function1 <- function(args, body, env = parent.frame()) { args <- as.pairlist(args) eval(call("function", args, body), env) }
Другой вариант - создать пустую функцию, а затем заполнить ее необходимыми значениями:
make_function2 <- function(args, body, env = parent.frame()) { f <- function() {} formals(f) <- args body(f) <- body environment(f) <- env f }
Третий вариант - просто использовать as.function
:
make_function3 <- function(args, body, env = parent.frame()) { as.function(c(args, body), env) }
И, наконец, это кажется очень похожим на первый метод для меня, за исключением того, что мы используем несколько другую идиому для создания вызова функции, используя substitute
а не call
:
make_function4 <- function(args, body, env = parent.frame()) { subs <- list(args = as.pairlist(args), body = body) eval(substitute(`function`(args, body), subs), env) } library(microbenchmark) microbenchmark( make_function1(args, body), make_function2(args, body), make_function3(args, body), make_function4(args, body), function(a = 1, b = 2) a + b ) Unit: nanoseconds expr min lq median uq max 1 function(a = 1, b = 2) a + b 187 273.5 309.0 363.0 673 2 make_function1(args, body) 4123 4729.5 5236.0 5864.0 13449 3 make_function2(args, body) 50695 52296.0 53423.0 54782.5 147062 4 make_function3(args, body) 8427 8992.0 9618.5 9957.0 14857 5 make_function4(args, body) 5339 6089.5 6867.5 7301.5 55137
Существует также проблема создания alist
объектов программно, поскольку это может быть полезно для создания функций, когда число аргументов является переменным.
alist
– просто именованный список пустых символов. Эти пустые символы могут быть созданы с помощью substitute()
. Так:
make_alist <- function(args) { res <- replicate(length(args), substitute()) names(res) <- args res } identical(make_alist(letters[1:2]), alist(a=, b=)) ## [1] TRUE