Подтвердить что ты не робот

Используя внутреннюю привязку, внутри функции

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

В 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 в качестве параметров).

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

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

Хуан

4b9b3361

Ответ 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')

Ответ 2

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

foo <- function(a=1, b=5, h='coconut') {
  as.list(environment())
}
foo(a = 0, h = 'mango')

Ответ 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)
}