exists and sapply:为什么这些功能不同?

exists and sapply: why are these functions different?

为什么下面的两个函数fngn不同?我不认为他们应该是,但我一定遗漏了一些东西。

vars <- letters[1:10]
a <- b <- 1
fn <- function (d) {
    sapply( vars, exists )
}
gn <- function (d) {
    sapply( vars, function (x) { exists(x) } )
}
fn(d=2)
#    a     b     c     d     e     f     g     h     i     j 
# TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE 
gn(d=2)
#    a     b     c     d     e     f     g     h     i     j 
# TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE 
exists("i")
# [1] FALSE

有两个区别:

  1. gn(d=2)d 存在,但为什么 fn(d=2) 不存在?
  2. fn(d=2) 表示 i 存在,而 gn(d=2) 不存在。这很令人费解,因为我没有在任何地方定义 i

注意:这是在 R 版本 3.2.0 上,似乎第二个行为是该版本的新行为(见下文)。

关于i,我无法重现。但我会尝试解释其他差异,首先是两个函数都找到的 c 和只有 gn() 找到的 d

两个函数都找到了 c,因为它们正在寻找基函数 c

现在关于 d,R 是词法范围的。你可能想看看这个问题 Environments in R, mapply and get。因此,在第一个函数 fn() 中,exists 不是在 fn() 的局部环境中寻找 d,而是在全局环境中(并且没有d 那里)。

但是请注意,在 gn() 中,您将使用 exists() 的函数定义为 gn() 中的匿名函数。看:

gn <- function (d) {
    sapply( vars, function (x) { exists(x) } ) # defined anon function
}

所以gn()的环境就是匿名函数function (x) { exists(x) }的父环境。这就是为什么它在 gn 内搜索并找到局部参数 d,返回 TRUE.

重要的区别是 "d" 的值,即调用中正式变量的名称。 . exists 函数的默认设置是 "above" 而不是 "in"。在第一个实例中,.GlobalEnv 中没有 "d" 命名的实体,这是调用函数的地方。在第二种情况下,周围环境中有一个d(作为名称)。

正在寻找 "i" 或 "c" 的人正在寻找函数 c 或 for 循环的索引。 (索引变量在 for 循环结束时持续存在。)

看来 "i" 值差异仅出现在最新版本中,我在这台机器上没有看到,因为它在我当前的 OS 版本中已达到 3.1.3 的最大值。在当前会话中使用 "i" 作为 for 循环中的索引变量的人(或正在使用较早保存的环境)也可能发生这种情况。

这是奇怪的行为。我相信对此有一个合理的解释。这是一个全新的 R 会话。这次我什至没有使用 RStudio,它产生相同的输出。

R version 3.2.0 (2015-04-16) -- "Full of Ingredients"
Copyright (C) 2015 The R Foundation for Statistical Computing

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> exists('i')
[1] FALSE
> sapply('i', exists)
   i 
TRUE 

更奇怪的行为:

> lapply('i', exists)
[[1]]
[1] TRUE

> vapply('i', exists, logical(1))
   i 
TRUE 
> tapply('i', 'i', exists)
   i 
TRUE 
> mapply(exists, list('i'))
[1] FALSE
> rapply(list('i'), exists)
[1] FALSE
> exists('i')
[1] FALSE
> ls()
character(0)

mapplyrapply return FALSE。它们都需要列表作为参数。这可能与它有关。也许其他应用函数在 R 中的某些环境中选择了一些东西,而裸函数和一些应用函数则没有。

为什么 i 不一样...

看起来 R 3.2 中有变化。索引变量 ilapplyadded to the current environment(这是 sapply 实际调用的)。这与新行为一起强制评估传递给您正在应用的函数的参数。这意味着您现在可以访问循环中当前迭代的索引。

fngn 行为不同的原因是 exists() 在调用它的环境中查找。在 fn 的情况下,这是创建此 i 变量的环境。在 gn 的情况下,它正在查找匿名函数的环境。当 R 在本地环境中找不到符号时,它会根据定义函数的位置而不是调用它们的位置来搜索环境。这意味着 R 将找不到 i 变量,因为您的匿名函数是在 i 变量不存在的地方定义的。

我们可以写个小辅助函数,方便抓取当前索引

idx <- function() get("i", parent.frame(2))
sapply(letters[1:3], function(x) paste(idx(), x))
#     a     b     c 
# "1 a" "2 b" "3 c"

据我所知,这是目前未记录的行为。它可能会在 R 的未来版本中发生变化。

为什么 d 不一样...

d 变量的差异是一个更直接的范围问题。 R 再次创建一个新环境,用于调用函数 exists。此环境的父级是基础环境。因此,当您调用 exists 时,它会查找从何处调用它(这是存在 i 的环境)并且由于它在那里找不到 d ,因此它会搜索下一个父级基地环境。从不搜索当前函数环境。您可以使用

明确搜索当前环境
fn <- function (d) {
    sapply( vars, exists, where=environment() )
}
fn(d=2)
#    a     b     c     d     e     f     g     h     i     j 
# TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE 

有关 R 环境的更多信息,我建议您阅读 Environments section of Advanced R