关于“环境嵌套”/词法范围,我很难弄清楚这里到底发生了什么:
问题
where
函数中参数的默认值getClasses()
似乎会有所不同,具体取决于getClasses()
是在标准 R 函数中调用还是在正式 S4 方法中调用。它被.externalCallerEnv()
哪个似乎是惰性评估的“对象”控制,从而导致变化(见下面的编辑)
问题
从正式的 S4 方法内部调用时,如何设置与在标准函数内部调用where
时的默认值相同的值?getClasses()
插图
您将在下面找到上述“问题行为”的简短说明
1) 自定义类
我有许多当前来自.GlobalEnv
.
让我们以这一个作为他们所有人的代表
setRefClass("A", fields=list(x="numeric"))
2)列出可用的类
通过 argument where
, functiongetClasses
让我选择寻找类的环境。
以下似乎无处不在.GlobalEnv
,因此找不到我的班级;没关系:
classes <- getClasses()
> head(classes)
[1] "(" ".environment" ".externalptr" ".name" ".NULL"
[6] ".Other"
> "A" %in% classes
[1] FALSE
现在我只.GlobalEnv
查找类A
;这也很好:
classes <- getClasses(where=.GlobalEnv)
> classes
[1] "A"
> "A" %in% classes
[1] TRUE
3) 创建自定义标准查找函数
当我将查找通过getClasses
放入标准函数中时(这只是所需功能的第一部分,我想getClasses()
在该方法内部进行计算,而不是将其返回值作为形式参数传递),一切仍然正常
foo1 <- function(where=.GlobalEnv) {
if (is.null(where)) {
x <- getClasses()
} else {
x <- getClasses(where=where)
}
return(x)
}
> foo1()
[1] "A"
> classes <- foo1(where=NULL)
> head(classes)
[1] "(" ".environment" ".externalptr" ".name" ".NULL"
[6] ".Other"
> "A" %in% classes
[1] FALSE
4) 创建正式的 S4 方法
但是,一旦我将所有内容都放入正式的 S4 方法getClasses()
中,用于查找类的标准环境似乎发生了一些变化
setGeneric(
name="foo2",
signature="x",
def=function(x, ...) standardGeneric("foo2")
)
setMethod(
f="foo2",
signature=signature(x="missing"),
definition=function(
x,
where=.GlobalEnv
) {
if (is.null(where)) {
x <- getClasses()
} else {
x <- getClasses(where=where)
}
return(x)
}
)
[1] "foo2"
> foo2()
[1] "A"
> classes <- foo2(where=NULL)
> head(classes)
[1] "A" "(" ".environment" ".externalptr" ".name"
[6] ".NULL"
> "A" %in% classes
[1] TRUE
以前,"A" %in% foo1(where=NULL)
是FALSE
(需要),而现在是("A" %in% foo2(where=NULL)
不需要)。TRUE
任何想法如何foo2()
以完全相同的方式表现foo1()
?
编辑 2012-08-29
正如 Josh O'Brien 在下面的评论中指出的那样,这种变化可能是由惰性评估引起的。
调试foo1()
debug(getClasses)
foo1(where=NULL)
你进入调试跟踪器;点击<RETURN>
4 次,然后输入get("where")
:
Browse[2]> get("where")
<environment: namespace:base>
在控制台中,点击<RETURN>
1 次,然后键入evList
:
Browse[2]> evList
[[1]]
<environment: namespace:base>
键入Q
以退出当前调试运行
现在再次运行所有内容,但调试调用略有不同
foo1(where=NULL)
在控制台中,点击<RETURN>
5 次,然后键入evList
:
Browse[2]> evList
[[1]]
<environment: namespace:methods>
现在输入get("where")
:
Browse[2]> get("where")
<environment: namespace:methods>
现在where
指向namespace:methods
调试`foo2()'
foo2(where=NULL)
你进入调试跟踪器;点击<RETURN>
4 次,然后输入get("where")
:
Browse[2]> get("where")
<environment: namespace:base>
然后点击<RETURN>
1 次,然后键入evList
:
Browse[2]> evList
[[1]]
<environment: namespace:base>
键入Q
以退出当前调试运行
现在再次运行所有内容,但调试调用略有不同
foo2(where=NULL)
点击<RETURN>
5 次,然后输入evList
:
Browse[2]> evList
[[1]]
<environment: 0x02a68db8>
[[2]]
<environment: R_GlobalEnv>
# [OMITTED]
[[8]]
<environment: package:methods>
attr(,"name")
[1] "package:methods"
attr(,"path")
[1] "R:/Apps/LSQMApps/apps/R/R-2.14.1/library/methods"
[[9]]
<environment: 0x01e8501c>
attr(,"name")
[1] "Autoloads"
[[10]]
<environment: namespace:base>
现在输入get("where")
:
Browse[2]> get("where")
<environment: 0x02a68db8>
evList
并注意与where
之前的调试运行相比的不同值。键入Q
以退出当前调试运行。
这对我来说似乎有些奇怪,但从语言设计者的角度来看可能是有道理的。一旦我知道如何显式设置where
为指向与namespace:methods
.