1. ホーム
  2. r

[解決済み] Rのコピーオンモディファイセマンティクスとは何か、そしてカノニカルソースはどこか?

2023-07-08 04:10:14

質問

ときどき、Rが "undefined "であるという考え方に出くわします。 コピーオンモディファイセマンティクス という考え方に出くわしますが、例えば Hadley の devtools wiki です。 .

<ブロッククオート

ほとんどのRオブジェクトはコピーオンモディフィーのセマンティクスを持っているので、関数 の引数を変更しても元の値は変更されません。

この用語はR-Helpメーリングリストまでさかのぼることができます。 例えば、Peter Dalgaard は次のように書いています。 2003 年 7 月 :

<ブロッククオート

Rは関数型言語であり、遅延評価と弱い動的型付け(変数の型が自由に変えられる) 型付け(変数は自由に型が変えられる:a <- 1 ; a <- "a" が可能)を持つ関数型言語である。 が許される)。意味的には、すべてがコピーオンモディファイです。 意味的にはすべてコピーオンモディファイですが、最悪の非効率を避けるために、実装ではいくつかの最適化のトリックが使われています。 非効率を避けるために、実装ではいくつかの最適化のトリックが使われています。

同様に、Peter Dalgaardは、次のように書いています。 2004年1月 :

<ブロッククオート

Rはコピーオンモディフィーのセマンティクスを持っています(原則的に、そして時には実際に)。 そのため、一度オブジェクトの一部が変更されると、それを含んでいたものを新しい場所で探さなければならないかもしれません。 オブジェクト自体を含む、それを含んでいたものを新しい場所で探す必要があるかもしれません。 オブジェクト自体も含まれます。

さらにさかのぼると 2000 年 2 月 ロス・イハカは言った。

これを実現するために、私たちはかなりの労力を費やしました。 私は次のように説明します。 コピー・オン・モディファイ(必要なら)" と表現しています。 コピーが行われるのは オブジェクトが変更されたときのみ行われます。 (if necessary)の部分は、もし 変更によって非ローカル変数が変更されないことを証明できるのであれば 変数を変更できないと証明できれば、コピーせずにそのまま変更することを意味します。

マニュアルにない

どんなに探しても、"copy-on-modify" への言及が、以下のマニュアルに見当たりません。 R マニュアル にも、また R言語定義 でもなく R インターナル

質問

私の質問には2つの部分があります。

  1. これはどこで正式に文書化されているのですか?
  2. copy-on-modify はどのように機能するのですか?

例えば、quot;参照渡しについて話すことは適切でしょうか。 約束 は関数に渡されるからです。

どのように解決するのですか?

コール・バイ・バリュー

この場合 R言語定義 にはこのように書かれています (セクション 4.3.3 引数の評価 )

Rの引数で関数を呼び出す場合のセマンティクスは コール・バイ・バリュー . 一般に、与えられた引数は、与えられた値と対応する正式な引数の名前で初期化された局所変数であるかのように振る舞います。 関数内で供給された引数の値を変更しても、呼び出し側のフレームにある変数の値には影響しません。 . [強調表示]

というメカニズムが説明されていないのに対して コピーオンモディファイ が動作するメカニズムについては記述していませんが、関数に渡されたオブジェクトを変更しても、呼び出し元のフレームには影響しないことを述べています。

追加情報、特に コピーオンモディファイ アスペクトは SEXP の中にある R インターナルマニュアル というセクションがあります。 1.1.2 残りのヘッダー . 具体的には、次のように書かれています。

named フィールドが設定され、それにアクセスするために SET_NAMEDNAMED マクロを使用し、値を 0 , 12 . R には '値で呼び出す' のイリュージョンを持つので、以下のような代入は

b <- a

のコピーを作成するようです。 a として参照し b . しかし であれば も a でも b がその後変更された場合、コピーする必要はありません。 実際に起こることは、新しいシンボル b が同じ と同じ値に a と同じ値で named フィールドが設定されます (この場合 に設定されます。 2 ). オブジェクトが変更されようとすると named フィールド が参照される。の値は 2 という値は、オブジェクトが変更される前に複製されなければならないことを意味します。 を意味します。(これは複製が必要であるとは言っていないことに注意してください。 複製する必要がある,とは言っていません。 複製する必要があることを意味するものではないことに注意してください)。の値は 0 という値は、他の SEXP はこのオブジェクトとデータを共有していないことが分かっているので、安全に変更できることを意味します。 の値は 1 のような状況で使用されます。

dim(a) <- c(7, 2)

というように、原則的に2つのコピーが存在します。 として計算されます(原理的には)

a <- `dim<-`(a, c(7, 2))

というように、いくつかの原始的な関数は、この場合のコピーを避けるために最適化することができます。 この場合、コピーを避けるように最適化することができます。

これはオブジェクトが引数として関数に渡される状況を説明していませんが、特に先に引用したR言語の定義からの情報を考えると、同じプロセスが動作していると推論できるかもしれません。

関数評価における約束事

というのはあまり正しくないと思います。 プロミス 渡された を関数に渡します。引数は関数に渡され,実際に使われる式はプロミスとして格納されます(さらに呼び出し環境へのポインタも格納されます).引数が評価されるときだけ、プロミスに格納された式が取り出され、ポインタによって示される環境内で評価されます。 強制 .

について語ることは、正しいとは思えません。 パス・バイ・リファレンス という言い方は正しくないと思います。Rには コール・バイ・バリュー のセマンティクスを持ちますが、引数に渡された値が評価され変更されない限り、コピーを避けようとします。

NAMED メカニズムは最適化であり (コメントで @hadley が指摘したように)、変更時にコピーを作成する必要があるかどうかを R が追跡できるようにするものです。NAMED 機構がどのように動作するかについては、Peter Dalgaard 氏が議論したように、いくつかの微妙な点があります ( R開発スレッド mnel が質問に対する彼らのコメントで引用している)