Dans le livre Logiciel pour l'analyse des données : Programmation avec R , John Chambers insiste sur le fait que les fonctions ne doivent généralement pas être écrites pour leur effet secondaire ; plutôt, qu'une fonction doit retourner une valeur sans modifier aucune variable dans son environnement d'appel. Inversement, l'écriture d'un bon script utilisant des objets data.table devrait spécifiquement éviter l'utilisation de l'assignation d'objet avec <-
La fonction de gestion des données, généralement utilisée pour stocker le résultat d'une fonction.
Tout d'abord, une question technique. Imaginez une fonction R appelée proc1
qui accepte un data.table
objet x
comme argument (en plus, peut-être, d'autres paramètres). proc1
retourne NULL mais modifie x
en utilisant :=
. D'après ce que j'ai compris, proc1
en appelant proc1(x=x1)
fait une copie de x1
juste à cause de la façon dont les promesses fonctionnent. Cependant, comme démontré ci-dessous, l'objet original x1
est toujours modifié par proc1
. Pourquoi/comment est-ce le cas ?
> 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
>
En outre, il semble que l'utilisation proc1(x=x1)
n'est pas plus lent que d'exécuter la procédure directement sur x, ce qui indique que ma vague compréhension des promesses est erronée et qu'elles fonctionnent d'une manière pass-by-reference :
> 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
Ainsi, étant donné que le fait de passer un argument data.table à une fonction n'ajoute pas de temps, il est possible d'écrire des procédures pour les objets data.table, en incorporant à la fois la vitesse de data.table et la généralisation d'une fonction. Cependant, étant donné ce que John Chambers a dit, à savoir que les fonctions ne devraient pas avoir d'effets secondaires, est-il vraiment "correct" d'écrire ce type de programmation procédurale dans R ? Pourquoi soutient-il que les effets secondaires sont "mauvais" ? Si je ne tiens pas compte de ses conseils, à quels pièges dois-je être attentif ? Que puis-je faire pour écrire de "bonnes" procédures data.table ?