問題が発生している理由は、オブジェクトを関数のローカル名前空間に渡しているためです。これは、Rの優れた/ひどいことの1つです。暗黙的な変数宣言を許可し、名前空間が深くなるにつれて優先順位を実装します。
関数が現在の名前空間内に新しい名前空間を作成するため、これは影響を及ぼします。オブジェクト「myTable」は、もともとグローバル名前空間で作成されたと思いますが、関数「title.asterisk」に渡されると、新しい関数ローカル名前空間に同じプロパティを持つオブジェクトが含まれるようになります。これは次のように機能します。
title.asterisk <- function(myTable){ do some stuff to 'myTable' }
この場合、関数'title.asterisk'はグローバルオブジェクト'myTable'に変更を加えません。代わりに、ローカルオブジェクトが同じ名前で作成されるため、ローカルオブジェクトがグローバルオブジェクトに優先します。このように関数を呼び出すと、関数title.asterisk(myTable)
はローカル変数にのみ変更を加えます。
グローバルオブジェクトを変更するには、2つの直接的な方法(および多くの間接的な方法)があります。
オプション1:最初に、あなたが言及したように、次のように、関数にオブジェクトを返し、グローバルオブジェクトを上書きさせることです。
title.asterisk <- function(myTable){
do some stuff to 'myTable'
return(myTable)
}
myTable <- title.asterisk(myTable)
これは問題ありませんが、実際には2つの異なる「myTable」オブジェクトがあり、1つは関数に対してグローバルで、もう1つはローカルであるため、コードを理解するのが少し難しくなっています。多くのコーダーは、ピリオド「。」を追加することでこれを明確にします。次のように、可変引数の前に:
title.asterisk <- function(.myTable){
do some stuff to '.myTable'
return(.myTable)
}
myTable <- title.asterisk(myTable)
これで、2つの変数が異なるという視覚的な手がかりが得られました。後でコードをデバッグしようとするときに、名前空間の優先順位などの目に見えないものに依存したくないので、これは良いことです。それは物事を必要以上に難しくします。
オプション2:関数内からオブジェクトを変更するだけです。これは、オブジェクトに破壊的な編集を行い、メモリを膨らませたくない場合に適したオプションです。破壊的な編集を行う場合は、元のコピーを保存する必要はありません。また、オブジェクトが適切に大きい場合は、必要のないときにコピーする必要はありません。グローバル名前空間オブジェクトを編集するには、関数に渡したり、関数内から宣言したりしないでください。
title.asterisk <- function(){ do some stuff to 'myTable' }
現在、関数内からオブジェクト'myTable'を直接編集しています。オブジェクトを渡していないという事実により、関数は変数名を解決するために、より高いレベルの名前空間を参照します。見よ、それは「myTable」オブジェクトをより高い位置で見つけます!関数のコードは、オブジェクトに変更を加えます。
考慮すべき注意:私はデバッグが嫌いです。つまり、デバッグは本当に嫌いです。これは、Rで私にとっていくつかのことを意味します。
- ほとんどすべてを関数でラップします。コードを書くとき、作品が機能するようになったらすぐに、それを関数でラップして脇に置きます。私は「。」を多用します。すべての関数引数にプレフィックスを付け、それが存在する名前空間にネイティブなものにはプレフィックスを使用しません。
- 関数内からグローバルオブジェクトを変更しないようにしています。私はこれがどこにつながるのか好きではありません。オブジェクトを変更する必要がある場合は、それを宣言した関数内から変更します。これは多くの場合、関数を呼び出す関数のレイヤーがあることを意味しますが、モジュール式で理解しやすいものになります。
- すべてのコードにコメントし、各行またはブロックの目的を説明します。少し無関係に思えるかもしれませんが、これら3つのことが私にとってはうまくいくことがわかりました。コーディングを関数でラップし始めると、古いコードをもっと再利用したいと思うようになります。そこで良いコメントが入ります。私にとって、それは必要な部分です。