3

「環境の入れ子」/字句スコープに関して、ここで正確に何が起こっているのかを理解するのに苦労しています:

問題

where関数内の引数のデフォルト値は、標準の R 関数内で呼び出されるか、正式な S4 メソッドgetClasses()内で呼び出されるかによって異なるようです。遅延評価の「オブジェクト」と思われるものによって制御されるため、バリエーションが発生します(以下の編集を参照)getClasses().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>

前に実行したデバッグevListと比較して、と の値が異なることに注意してください。whereと入力Qして、現在のデバッグ実行を終了します。

これは私にはやや奇妙に思えますが、おそらく言語設計者の観点からは理にかなっています。whereに関連付けられた環境を指すように明示的に設定する方法がわかれば、おそらく大丈夫でしょうnamespace:methods

4

1 に答える 1

2

よく知られた教訓をもう一度学びました: 明示的であることは常に良いことです ;-)

Josh O'BrienとEtiennebrによるこの投稿のおかげで、これらのピースをまとめることができたと思います。

遅延評価レキシカル スコープにより、通常の関数のいずれかまたは内部から呼び出された場合と同じように動作することを実際に確認 する唯一の方法は、正式な S4 内で呼び出すときに関連付けられている環境にの値を明示的に設定することだと思います方法。getClasses().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 に答える