`getClasses()`中的默认环境的模糊变化(标准函数与正式S4方法)

时间:2012-08-28 19:57:45

标签: r class default-value s4 lexical-scope

我在查找“环境嵌套”/词汇范围方面究竟发生了什么方面遇到了一些麻烦:

问题

函数where中参数getClasses()的默认值似乎取决于是否在标准R函数内调用getClasses()正式的S4方法。它由.externalCallerEnv()控制,它似乎是懒惰评估的“对象”,从而导致变化(请参阅下面的编辑

问题

当从正式的S4方法内部调用时,如何在标准函数内调用where时将getClasses()设置为与默认值相同的值?


例证

下面您将找到上述“有问题的行为”的简短说明

1)自定义类

我有很多类定义,目前来自.GlobalEnv

让我们把这一个作为所有人的代表

setRefClass("A", fields=list(x="numeric"))

2)列出可用类

通过参数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

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>

并注意evListwhere与之前的调试运行相比的不同值。键入Q以退出当前的调试运行。

这对我来说似乎有点奇怪,但从语言设计师的角度来看可能是有道理的。一旦我知道如何明确地将where设置为指向与namespace:methods相关联的环境,我可能会没事。

1 个答案:

答案 0 :(得分:2)

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

感谢Josh O'Brienpost 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