我在查找“环境嵌套”/词汇范围方面究竟发生了什么方面遇到了一些麻烦:
函数where
中参数getClasses()
的默认值似乎取决于是否在标准R函数内调用getClasses()
或正式的S4方法。它由.externalCallerEnv()
控制,它似乎是懒惰评估的“对象”,从而导致变化(请参阅下面的编辑)
当从正式的S4方法内部调用时,如何在标准函数内调用where
时将getClasses()
设置为与默认值相同的值?
下面您将找到上述“有问题的行为”的简短说明
我有很多类定义,目前来自.GlobalEnv
。
让我们把这一个作为所有人的代表
setRefClass("A", fields=list(x="numeric"))
通过参数where
,函数getClasses
让我选择查找类的环境。
除了.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
当我通过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
但是,一旦我将所有内容都放入正式的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()
完全相同?
正如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(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
相关联的环境,我可能会没事。
答案 0 :(得分:2)
再次学到一个众所周知的教训:明确总是好的; - )
感谢Josh O'Brien和post Etiennebr,我想我能把各个部分放在一起。
由于延迟评估和词汇范围,我认为真正确保 getClasses()
的行为方式相同的唯一方法好像从.GlobalEnv
或从常规函数内部调用时,在正式S4方法中调用where
时,将namespace:methods
的值显式设置为与getClasses()
关联的环境
要获取与命名空间关联的环境,这似乎有效:
env <- loadNamespace("methods")
> is.environment(env)
[1] TRUE
或者甚至更好:
env <- asNamespace("methods")
> is.environment(env)
[1] TRUE
这个环境正是我们需要将where
指向
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(where=asNamespace("methods"))
} else {
x <- getClasses(where=where)
}
return(x)
}
)
> foo2()
[1] "A"
> classes <- foo2(where=NULL)
> head(classes)
[1] "(" ".environment" ".externalptr" ".name" ".NULL"
[6] ".Other"
> "A" %in% classes
[1] FALSE