3

关于“环境嵌套”/词法范围,我很难弄清楚这里到底发生了什么:

问题

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.

4

1 回答 1

2

又一次学到了一个众所周知的教训:明确一点总是好的;-)

多亏了Josh O'BrienEtiennebr的这篇文章,我想我能够将这些部分拼凑起来。

由于惰性求值词法作用域,我认为真正确保 行为与从常规函数中的任何一个或从常规函数内部getClasses()调用相同的唯一方法是在正式 S4 内部调用时显式设置相关环境的值方法。.GlobalEnvwherenamespace:methodsgetClasses()

要获得与命名空间关联的环境,这似乎可行:

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
于 2012-08-29T09:08:03.327 回答