著書「Software for Data Analysis: Programming with R」の中で、John Chambers は、関数は通常、副作用のために書かれるべきではないと強調しています。むしろ、関数は呼び出し環境で変数を変更せずに値を返す必要があります。逆に、data.table オブジェクトを使用して適切なスクリプトを作成する場合は<-
、通常は関数の結果を格納するために使用される でのオブジェクト割り当ての使用を明確に避ける必要があります。
まず、技術的な質問です。引数としてオブジェクトproc1
を受け入れるR 関数が呼び出されると想像してください (さらに、おそらく他のパラメーターも)。NULL を返しますが、を使用して変更します。私が理解していることから、呼び出しは、約束が機能する方法のためにコピーを作成します。ただし、以下に示すように、元のオブジェクトはまだ によって変更されています。なぜ/どうですか?data.table
x
proc1
x
:=
proc1
proc1(x=x1)
x1
x1
proc1
> require(data.table)
> x1 <- CJ(1:2, 2:3)
> x1
V1 V2
1: 1 2
2: 1 3
3: 2 2
4: 2 3
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> proc1(x1)
NULL
> x1
V1 V2 y
1: 1 2 2
2: 1 3 3
3: 2 2 4
4: 2 3 6
>
さらに、使用proc1(x=x1)
は x で直接プロシージャを実行するよりも遅くはないようです。これは、promise に関する私の漠然とした理解が間違っており、それらが参照渡しのような方法で機能することを示しています。
> x1 <- CJ(1:2000, 1:500)
> x1[, paste0("V",3:300) := rnorm(1:nrow(x1))]
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> system.time(proc1(x1))
user system elapsed
0.00 0.02 0.02
> x1 <- CJ(1:2000, 1:500)
> system.time(x1[,y:= V1*V2])
user system elapsed
0.03 0.00 0.03
したがって、data.table 引数を関数に渡しても時間がかからないことを考えると、data.table の速度と関数の一般化可能性の両方を組み込んで、data.table オブジェクトのプロシージャを作成できます。しかし、関数に副作用があってはならないという John Chambers の発言を考えると、この種の手続き型プログラミングを R で書くことは本当に「OK」なのだろうか? なぜ彼は副作用が「悪い」と主張したのですか? 彼のアドバイスを無視する場合、どのような落とし穴に注意すればよいですか? 「良い」data.table プロシージャを作成するにはどうすればよいですか?