R中with()和attach()之间的行为截然不同?

j_r*_*ker 11 r

人们经常使用attach()detach()功能建立在R参数名"搜索路径",但由于这种改变是很难跟踪,全局状态的人建议使用with()替代,其临时改变设置的搜索路径,持续时间一个表达式.

但是我只是注意到,不像attach(),with()显然没有"解决"功能.例如,让我们首先设置一个虚函数,它将访问一个名为的变量x:

f <- function { print(x) }
Run Code Online (Sandbox Code Playgroud)

现在,

with(list(x=42), f())
Run Code Online (Sandbox Code Playgroud)

尽管失败了

with(list(x=42), print(x))
Run Code Online (Sandbox Code Playgroud)

attach(list(x=42))
f()
Run Code Online (Sandbox Code Playgroud)

都成功了!:(

谁能告诉我为什么?我想with()表现得像attach()在这里一样,通过设置包含参数值的环境,使我能够有效地将大参数列表传递给函数with().我认为这种方法比替代方案有几个好处(我考虑过的两个方法是(a)费力地将所有参数传递给函数,以及(b)明确地将参数列表/框架作为函数参数传递并具有函数本身调用with()),但它不起作用.老实说,我发现这种差异非常麻烦!任何解释/帮助将不胜感激.

我正在使用R 2.11.1.

G. *_*eck 8

有什么区别with(list(x = 42), f())在做什么,你的期望之间的差别在词法作用域(这是R使用)和动态作用域(这似乎是你期待什么).

词汇范围是指自由变量(如变量xf)在环境中查找f定义 -不是环境f从所谓的.

f在全局环境中定义,以便x查找.

with调用创建一个新环境并不重要,因为调用它f的环境不涉及查找自由变量.

要以此方式工作,请创建一个副本f并重置其环境,因为这是R用于搜索自由变量的内容:

with(list(x = 42), { environment(f) <- environment(); f() })
Run Code Online (Sandbox Code Playgroud)

不可否认,这有点繁琐,但您可以通过使用proto包来简化它,因为proto重置了显式插入到proto对象中的每个函数的环境:

library(proto)
with(proto(x = 42, f = f), f())
Run Code Online (Sandbox Code Playgroud)

添加:

请注意,如果您的目标是进行面向对象编程(根据您对其他响应的评论),那么您可能希望在proto主页上进一步查看proto .例如,我们可以定义proto对象p并重新定义,f以便它的方法p(在这种情况下它必须接受参数1中的对象),如下所示:

library(proto)
p <- proto(x = 42, f = function(.) print(.$x))
p$f()
Run Code Online (Sandbox Code Playgroud)

增加2:

对于附加的案例,f()首先运行在全局环境中查找,因为这f是定义的位置.由于x在全球环境中找不到,因此它会查看全局环境的父级,在这种情况下,它会在那里找到它.我们可以发现使用全球环境的父级,在parent.env这里我们看到附加环境已经成为全球环境的父级.

> attach(list(x = 42))
> parent.env(.GlobalEnv)
<environment: 0x048dcdb4>
attr(,"name")
[1] "list(x = 42)"
Run Code Online (Sandbox Code Playgroud)

我们可以像这样查看全球环境及其所有祖先:

> search()
 [1] ".GlobalEnv"        "list(x = 42)"      "package:stats"    
 [4] "package:graphics"  "package:grDevices" "package:utils"    
 [7] "package:datasets"  "package:methods"   "Autoloads"        
[10] "package:base"   
Run Code Online (Sandbox Code Playgroud)

因此"list(x = 42)"是全局环境的父级,统计信息是父级"list(x = 42)",依此类推.


Rei*_*son 7

认为这是因为你没有定义任何参数f,因此x需要如何print(x)查找.

在正常使用中,如果没有提供它将在全局环境中f()查找x(并且它不是也不f是没有参数).

在内部with(),会发生的是,所需的任何参数f都将从data参数中获取.但是因为你f没有接受任何参数,所以x从不使用列表中的参数.相反,R恢复到通常的行为,并在全球环境x的环境中查找f,当然它不存在.不同之attach()处在于它明确地将包含a的对象添加x到全局环境中的搜索路径.

如果你正确地编写你的函数,遵循你在函数中使用的任何和所有参数传递的咒语,那么一切都按照预期的方式工作:

> F <- function(x) print(x)
> with(list(x = 42), F(x))
[1] 42
> ls()
[1] "f" "F"
Run Code Online (Sandbox Code Playgroud)

如果您已经有呼叫所需的列表或参数,可以考虑do.call()为您设置呼叫,而不是使用.例如:

> do.call(F, list(x = 42))
[1] 42
Run Code Online (Sandbox Code Playgroud)

您仍然需要使用参数正确定义函数,因为您f不起作用:

> do.call(f, list(x = 42))
Error in function ()  : unused argument(s) (x = 42)
Run Code Online (Sandbox Code Playgroud)