#r #lapply
#r #lapply
Вопрос:
это может показаться чрезмерно сложным вопросом, но на какое-то время это немного сводит меня с ума. Это также для любопытства, потому что у меня уже есть способ делать то, что мне нужно, так что это не так важно.
В R мне нужна функция для возврата именованного объекта list со всеми аргументами и значениями, введенными пользователем. Для этого я создал этот код (игрушечный пример):
foo <- function(a=1, b=5, h='coconut') {
frm <- formals(foo)
parms <- frm
for (i in 1:length(frm))
parms[[i]] <- get(names(frm)[i])
return(parms)
}
Итак, когда задается этот вопрос:
> foo(b=0)
$a
[1] 1
$b
[1] 0
$h
[1] "coconut"
Этот результат идеален. Дело в том, что когда я пытаюсь использовать lapply
с той же целью, чтобы быть немного более эффективным (и элегантным), это работает не так, как я хочу:
foo <- function(a=1, b=5, h='coconut') {
frm <- formals(foo)
parms <- lapply(names(frm), get)
names(parms) <- names(frm)
return(parms)
}
Проблема явно связана со средой, в которой get
вычисляется ее первый аргумент (символьная строка, имя переменной). Это я частично знаю из сообщения об ошибке:
> foo(b=0)
Error in FUN(c("a", "b", "h")[[1L]], ...) : object 'a' not found
а также, потому что, когда в .GlobalEnv
среде есть объекты с правильными именами, foo возвращает их значения вместо:
> a <- 100
> b <- -1
> h <- 'wallnut'
> foo(b=0)
$a
[1] 100
$b
[1] -1
$h
[1] "wallnut"
Очевидно, что, поскольку get
по умолчанию выполняется вычисление в parent.frame()
, он выполняет поиск объектов в .GlobalEnv
среде, а не в текущей функции. Это странно, поскольку этого не происходит с первой версией функции.
Я перепробовал множество вариантов, чтобы заставить функцию get
оценивать в нужной среде, но не смог сделать это правильно (я пробовал pos=-2,0,1,2
и envir=NULL
в качестве опций).
Если кто-нибудь знает немного больше меня об средах, особенно в этих «странных» случаях, я хотел бы знать, как это решить.
Спасибо за ваше время,
Хуан
Ответ №1:
Правка 2013-08-05
Использование sapply()
вместо lapply()
значительно упрощает это:
foo4 <- function(a=1, b=5, h='coconut') {
frm <- formals(sys.function())
sapply(names(frm), get, envir=sys.frame(sys.parent(0)), simplify=FALSE)
}
foo4(b=0, h='mango')
Это, однако, без sapply()
или lapply()
может быть более элегантным решением:
foo5 <- function(a=1, b=5, h='coconut') {
modifyList(formals(sys.function()), as.list(match.call())[-1])
}
foo5(b=0, h='mango')
Оригинальное сообщение (2011-11-04)
После небольшого анализа это выглядит лучшим решением.
foo <- function(a=1, b=5, h='coconut') {
frm <- formals(foo)
parms <- lapply(names(frm), get, envir=sys.frame(sys.parent(0)))
names(parms) <- names(frm)
return(parms)
}
foo(b=0, h='mango')
# $a
# [1] 1
# $b
# [1] 0
# $h
# [1] "mango"
Здесь есть некоторые тонкие вещи, связанные с тем, как lapply
определяет / оценивает вызовы, которые он создает. Подробности скрыты в вызове .Internal(lapply(X, FUN))
, но для вкуса сравните эти два вызова:
# With function matched by match.fun, search in sys.parent(0)
foo2 <- function(a=1, h='coconut') {
lapply(names(formals()),
get, envir = sys.parent(0))
}
# With anonymous function, search in sys.parent(2)
foo3 <- function(a=1, h='coconut') {
lapply(names(formals()),
FUN = function(X) get(X, envir = sys.parent(2)))
}
foo4(a=0, h='mango')
foo5(a=0, h='mango')
Комментарии:
1. …еще лучше, если
foo2
иfoo3
получили свои собственные формалы, а неfoo
‘ы 🙂2. Джошу: было бы то же самое, если бы я использовал
envr=sys.parent() 1
?3. @Juan — Спасибо, кстати, за отличный оригинальный пост. Это тоже сводило меня с ума, в течение нескольких часов!
4. Вместо
sys.frame(sys.parent(0))
, я думаюenvironment()
, немного более очевидно. На самом деле вы могли бы заменить всю эту строку наmget(names(frm), environment())
5. Или вы могли бы заменить всю функцию на
as.list(environment())
Ответ №2:
Просто преобразуйте текущую среду в список:
foo <- function(a=1, b=5, h='coconut') {
as.list(environment())
}
foo(a = 0, h = 'mango')
Комментарии:
1. Да, круто, вероятно, это именно то, чего на самом деле добивалась операция.
Ответ №3:
Это адаптировано из решения @Josh O’Brien, приведенного выше, с использованием sapply
для автоматического присвоения правильных имен результирующему списку (сохраняется одна строка кода):
foo <- function(a=1, b=5, h='coconut') {
frm <- formals(foo)
parms <- sapply(names(frm), get, envir=sys.frame(sys.parent(-1)), simplify=FALSE)
return(parms)
}