为什么'with'不通过嵌套函数传递变量范围?

nsh*_*eff 5 scope r

在R中,如果我创建一个环境然后用于with评估该环境中的函数,则该函数通常可以访问变量.但是,如果我嵌套函数,由于某种原因,它们会超出范围.你能解释一下为什么会这样吗?

例:

使用名为的变量创建新环境 x

E = new.env();
E$x = c(1,2,3)
Run Code Online (Sandbox Code Playgroud)

使用with我可以打印这个变量:

with(E, print(x));
#[1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

但是现在如果我嵌套这个函数,它就不再起作用了:

printMe = function() { print(x); }
with(E, printMe())
#Error in print(x) : object 'x' not found
Run Code Online (Sandbox Code Playgroud)

我知道我可以让它再次起作用:

printMe = function(x) { print(x); }
with(E, printMe(x))
#[1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

但我不明白 - 如果with创建一个环境,为什么嵌套函数看不到x?如果你附上它可以工作:

attach(E)
printMe()
#[1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

我想我只是缺少一些关于范围界定的东西,但推荐的方法是什么?或者,以另一种方式提出我的问题:为什么不能在with访问自由变量中嵌套函数?

MrF*_*ick 7

基本上,当你使用时with,你也会这样做

printMe = function() { print(x); }
local({
    x=1:3
    printMe()
})
# Error in print(x) : object 'x' not found
Run Code Online (Sandbox Code Playgroud)

这也行不通.这与函数中如何解析自由变量有关.当你调用时printMe,它将寻找解决外壳本身中的变量,然后它查找定义函数的父框架(它看不到调用函数的位置).这里,printMe是在全球环境中定义的.但是,x并未在全球环境中定义.如果你这样做

printMe = function() { print(x); }
x=1:3
printMe()
# [1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

那么这两个xprintMe在全球环境中定义,因此它的工作原理.您还可以更改该printMe功能的环境

 environment(printMe) <- E
 printMe()
 # [1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

或者在与with相同的环境中定义函数

with(E,{printMe <- function() {print(x)}; printMe()})
# [1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

重点是,您调用函数的环境无关紧要,重要的是它们所定义的环境.

您可能想要查看有关这些属性的函数式编程的Advanced R材料.


Car*_*lli 4

R 是词法作用域而不是动态作用域。的环境printMeGlobal Environment,因为它是在Global Environment中定义的:

\n\n
environment(printMe)\n<environment: R_GlobalEnv>\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以当你打电话时:

\n\n
with(E, printMe())\n
Run Code Online (Sandbox Code Playgroud)\n\n

该函数printMe尝试在本地查找x。它没有\xc2\xb4t。然后它尝试在其环境中查找x,这是全局环境,而不是的本地环境with。它再次找不到它,然后抛出错误。

\n\n

为了说明这一点,请注意,如果您printMe在 inside 中定义with,则 的环境printMe将是 的本地环境with,并且它将找到x

\n\n
with(E,{\n  printMe <- function() {\n    print(x)\n  } \n  print(environment(printMe))\n  printMe()\n  })\n<environment: 0x29785678>\n[1] 1 2 3\n
Run Code Online (Sandbox Code Playgroud)\n\n

printMe或者,您可以更改内部环境with

\n\n
with(E, {\n  environment(printMe) <- environment()\n  printMe()\n})\n[1] 1 2 3\n
Run Code Online (Sandbox Code Playgroud)\n\n

关于第二个示例,当您使用attach环境时,您正在使(即)E的对象可用于全局环境。因此,当您在这种情况下调用时,它会在全局环境中查找,并且由于已附加,因此它会找到它。这就是它起作用的原因。ExprintMexE

\n\n

我曾经和你有同样的疑问,所以这个问题可能会进一步帮助你:Environments in R, mapply and get

\n\n

这也可能有帮助: Understanding lexical scoping in R

\n