13

这似乎是一个过于复杂的问题,但它让我有些发疯了一段时间。也是出于好奇,因为我已经有了做我需要的方法,所以没那么重要。

在 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,2envir=NULL作为选项)。

如果有人碰巧比我更了解环境,特别是在这种“奇怪”的情况下,我很想知道如何解决这个问题。

谢谢你的时间,

胡安

4

3 回答 3

11

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')
于 2011-11-04T23:41:29.277 回答
7

Just convert the current environment into a list:

foo <- function(a=1, b=5, h='coconut') {
  as.list(environment())
}
foo(a = 0, h = 'mango')
于 2013-08-07T01:53:12.887 回答
1

这改编自上述@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)
}
于 2011-11-04T23:35:08.417 回答