【问题标题】:Using get inside lapply, inside a function在函数内部使用 get inside lapply
【发布时间】:2011-12-22 10:20:51
【问题描述】:

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

在 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 作为选项)。

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

感谢您的宝贵时间,

胡安

【问题讨论】:

    标签: r lapply


    【解决方案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')
    

    【讨论】:

    • ...如果 foo2foo3 有自己的正式形式,而不是 foo 的形式,那就更好了 :-)
    • 致乔希:如果我使用envr=sys.parent() + 1会不会一样?
    • @Juan -- 顺便说一句,谢谢你的精彩原创文章。它也让我发疯了,在那里呆了几个小时!
    • 我认为sys.frame(sys.parent(0)) 不是environment() 更明显一点。实际上,您可以将整行替换为 mget(names(frm), environment())
    • 或者你可以用as.list(environment())替换整个函数
    【解决方案2】:

    只需将当前环境转换成列表即可:

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

    【讨论】:

    • 是的,很酷,这可能正是 OP 真正想要的。
    【解决方案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)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-18
      • 1970-01-01
      • 1970-01-01
      • 2017-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-04
      相关资源
      最近更新 更多