关于“环境嵌套”/词法范围,我很难弄清楚这里到底发生了什么:
问题
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.