8

この質問は次の質問を促しました:呼び出しスタックにある特別なプリミティブ関数を表示する方法はありますか?

たとえば、終了時に呼び出しスタックを返す関数を作成します。

myFun <- function(obj){
  on.exit(print(sys.calls()))
  return(obj)
}

この関数を呼び出し、を使用してその結果をオブジェクトに割り当てると、assign特別なプリミティブ関数を使用する必要がなくなります。

> assign("myObj",myFun(4))
[[1]]
assign("myObj", myFun(4))

[[2]]
myFun(4)

しかし、代入演算子を使用すると、これはスタックから除外されます

> `<-`(myObj, myFun(6))
[[1]]
myFun(6)

確かに、呼び出しスタックで代入演算子を確認することはそれほど一般的ではないかもしれませんが、などの他の関数も非表示になりrepますlog

4

2 に答える 2

9

呼び出しスタックを介してプリミティブ関数への呼び出しにアクセスする方法はないと思います。これが理由です。

「典型的な」R関数が評価されるとき:

  1. 指定された引数は、正式な引数と一致します。
  2. 新しい環境(それを囲む環境へのポインターを含む)が作成され、正式な引数がそれに割り当てられます。
  3. 関数の本体は、新しく作成された環境で評価されます。

sys.calls()関数呼び出しが相互にネストされているときに構築される囲み環境のチェーンは、sys.frames()などが何らかのアクセスを提供する「呼び出しスタック」または「フレームスタック」です。

私の強い疑いは、評価中にR側の環境が作成されないため、プリミティブ関数の呼び出しが呼び出しスタックに表示されないことです。環境は作成されないため、コールスタックに環境は表示されません。

さらに詳しい洞察を得るために、JohnChambersがデータ分析用ソフトウェアの464ページにあるプリミティブ関数の評価について説明しています。

これらの関数の1つへの呼び出しの評価は通常の方法で始まりますが、評価者が関数オブジェクトがRで定義された関数ではなくプリミティブであることを発見すると、まったく異なる計算に分岐します。オブジェクトは、正式な引数を持つ関数オブジェクトと、文字列引数を持つ関数.Primitive()の呼び出しであるように見えます。実際には、基本的に、Rのコアを実装するCコードの一部であるテーブルへのインデックスのみが含まれます。テーブルのエントリは、この特定のプリミティブへの呼び出しの評価を担当するコア内のCルーチンを識別します。評価者は制御をそのルーチンに移し、ルーチンが呼び出しの値を表すRオブジェクトへのC言語ポインターを返すことを期待します。

于 2012-10-24T22:31:25.823 に答える
3

Joshの答えは正しくないと思います。

さて、あなたの例のコールスタックにあったならそれは正しいでしょう。しかし、そうではありません<-

簡単な要約:通常のR関数の評価では、引数はアクセス時に遅延評価されるpromiseとして扱われます。これは、次の呼び出しで次のことを意味します。

foo(bar(baz))

bar(baz)内部 で評価されますfoo(あるとしても)。したがって、次のように、内部の呼び出しスタックを検査するとbar、次のようになります。

bar = function (x) {
    sys.calls()
}

…すると、次のようになります。

[[1]]
foo(bar(baz))

[[2]]
bar(baz)

残念ながら、ご指摘のとおり、<-(および=)は通常の関数ではなく、プリミティブ(BUILTINSXP)です。実際、Rソースでは次のように定義されています。

{"<-",      do_set,     1,  100,    -1, {PP_ASSIGN,  PREC_LEFT,   1}},

4番目の引数を見てください:100。このコードの前のコメントは、数字の意味を説明しています。左端の数字を説明する関連部分は次のとおりです。

Z = 1は、(BUILTINSXP)を呼び出す前に引数を評価すると言います

これは、次のコードの呼び出しが割り当てのbar(baz)に評価されることを意味します。

`<-`(x, bar(baz))

そのため<-、リストに表示されませんsys.calls():現在の呼び出しではありません。bar評価終了後に呼び出されます。


この制限を回避する方法があります。Rコードで<-/を再定義できます。=これを行うと、通常のR関数のように動作します。

`<-` = function (lhs, rhs) {
    name = as.name(deparse(substitute(lhs), backtick = true))
    rhs # evaluate expression before passing it to `bquote`, for a cleaner call stack
    eval.parent(bquote(base::`<-`(.(name), .(rhs))))
}

ただし、これにより、再定義されたスコープ内の後続のすべての割り当てで無視できないパフォーマンスヒットが発生することに注意してください<-。実際、割り当てはおよそ1000倍(!!!)遅くなります。これは通常受け入れられません。

于 2017-07-02T13:45:08.353 に答える