Использование get in lapply, внутри функции - PullRequest
12 голосов
/ 05 ноября 2011

это может показаться слишком сложным вопросом, но это заставляет меня немного сводить меня с ума в течение некоторого времени.Это также для любопытства, потому что у меня уже есть способ делать то, что мне нужно, так что это не так важно.

В R мне нужна функция для возврата именованного объекта списка со всеми аргументами и значениямивведено пользователем.Для этого я сделал этот код (пример игрушки):

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 в качестве опций).

Если кто-нибудь узнает немного больше, чем я, об окружающей среде, особенно в этих "странных" случаях, я хотел бы знать, как решить эту проблему.

Спасибо за ваше время,

Хуан

Ответы [ 3 ]

10 голосов
/ 05 ноября 2011

Редакция 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')
6 голосов
/ 07 августа 2013

Просто преобразовать текущую среду в список:

foo <- function(a=1, b=5, h='coconut') {
  as.list(environment())
}
foo(a = 0, h = 'mango')
1 голос
/ 05 ноября 2011

Это адаптировано из приведенного выше решения @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)
}
...