人们经常使用attach()
和detach()
函数为R中的变量名设置“搜索路径”,但因为这会改变难以跟踪的全局状态,people推荐using with()
instead,它会在单个表达式的持续时间内对搜索路径进行临时更改。
但是我注意到,与attach()
不同, with()
显然没有“解决”功能。例如,让我们首先设置一个虚拟函数来访问名为x
的变量:
f <- function { print(x) }
现在,
with(list(x=42), f())
即使,也会失败
with(list(x=42), print(x))
和
attach(list(x=42))
f()
都成功了! :(
谁能告诉我为什么?我希望with()
的行为与此处的attach()
完全相同,以便通过设置包含with()
参数值的环境,使我能够有效地将大参数列表传递给函数。我认为这种方法比替代方案有几个好处(我考虑过的两个方法是(a)费力地将所有参数传递给函数,以及(b)明确地将参数列表/框架作为函数参数传递并具有函数本身调用with()
),但它不起作用。老实说,我发现这种差异非常麻烦!任何解释/帮助将不胜感激。
我正在使用R 2.11.1。
答案 0 :(得分:8)
with(list(x = 42), f())
正在做什么和你期望的区别是 lexical scoping (这是R使用的)和动态范围之间的区别(似乎是你所期待的)。
词法范围意味着自由变量(如x
中的变量f
)会在f
定义的环境中查找< / em> - 不是环境f
是从调用的。
f
在全局环境中定义,因此查找x
。
调用with
来创建一个从中调用f
的新环境并不重要,因为调用它的环境不涉及查找自由变量。
要使其按照您想要的方式工作,请创建f
的副本并重置其环境,因为这是R用于搜索自由变量的内容:
with(list(x = 42), { environment(f) <- environment(); f() })
不可否认,这有点繁琐,但你可以通过使用proto包来简化它,因为proto
重置了显式插入到proto对象中的每个函数的环境:
library(proto)
with(proto(x = 42, f = f), f())
增加:
请注意,如果您的目标是进行面向对象的编程(根据您对其他响应的评论),那么您可能希望在proto home page进一步查看proto。例如,我们可以定义proto对象p
并重新定义f
,使其成为p
的方法(在这种情况下它必须接受参数1中的对象),如下所示:
library(proto)
p <- proto(x = 42, f = function(.) print(.$x))
p$f()
增加2:
对于附加的案例,首先运行f()
在全局环境中查找,因为这是定义f
的地方。由于在全局环境中找不到x
,因此它会查看全局环境的父级,在这种情况下,它会在那里找到它。我们可以使用parent.env
发现全局环境的父级,在这里我们看到附加环境已成为全局环境的父级。
> attach(list(x = 42))
> parent.env(.GlobalEnv)
<environment: 0x048dcdb4>
attr(,"name")
[1] "list(x = 42)"
我们可以按照以下顺序查看全球环境及其所有祖先:
> search()
[1] ".GlobalEnv" "list(x = 42)" "package:stats"
[4] "package:graphics" "package:grDevices" "package:utils"
[7] "package:datasets" "package:methods" "Autoloads"
[10] "package:base"
因此"list(x = 42)"
是全球环境的父级,统计信息是"list(x = 42)"
的父级,依此类推。
答案 1 :(得分:7)
我认为这是因为您没有定义f
的任何参数,因此x
所需的print(x)
是如何查找的。
在正常使用中f()
将在全局环境中查找x
(如果未提供)(并且它不是,也不能像f
那样不参数)。
在with()
内,会发生f
所需的任何参数都将来自data
参数。但由于您的f
不接受任何参数,因此从不使用列表中的x
。相反,R恢复到通常的行为,并在x
环境中查找f
,全球环境,当然它不在那里。与attach()
的区别在于它明确地将包含x
的对象添加到全局环境中的搜索路径。
如果你正确地编写了你的函数,遵循你传入函数中使用的任何和所有参数的咒语,那么一切都按照预期的方式工作:
> F <- function(x) print(x)
> with(list(x = 42), F(x))
[1] 42
> ls()
[1] "f" "F"
如果您已经有呼叫所需的列表或参数,可以考虑do.call()
为您设置呼叫,而不是使用。例如:
> do.call(F, list(x = 42))
[1] 42
您仍然需要使用参数正确定义函数,因为f
不起作用:
> do.call(f, list(x = 42))
Error in function () : unused argument(s) (x = 42)
答案 2 :(得分:3)
?with
州的描述:
在从数据构造的环境中评估R表达式, 可能会修改原始数据。
这意味着该函数在数据存在的环境中运行,但环境不在搜索路径上。因此,在此环境中运行的任何函数都不会找到未传递给它们的数据(因为它们在自己的环境中运行),除非指示查看parent.frame
。请考虑以下事项:
> f <- function() print(x)
> f()
Error in print(x) : object 'x' not found
> with(list(x=42),f())
Error in print(x) : object 'x' not found
> x <- 13
> f()
[1] 13
> with(list(x=42),f())
[1] 13
> f2 <- function(x) print(get("x",parent.frame()))
> f2()
[1] 13
> with(list(x=42),f2())
[1] 42
为了阐明通常如何使用with
,数据被传递给with
创建的环境中的函数,并且通常被调用的函数称为“全局”变量。 / p>
答案 3 :(得分:3)
传递参数列表对我有用:
f <- function(params) with(params, {
print(x)
})
f(list(x=42))
# [1] 42
但你应该考虑明确的引用,例如:
f <- function(params) {
print(params$x)
}
因为在开发阶段,有很多变量,你做的事情就是时间问题:
f <- function(params) with(params, {
# many lines of code
print(x)
})
x <- 7
f(list(y=8))
# [1] 7 # wasn't in params but you got an answer