「環境の入れ子」/字句スコープに関して、ここで正確に何が起こっているのかを理解するのに苦労しています:
問題
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
。