Je suis intéressé par la manière «correcte» d'écrire des fonctions avec des arguments optionnels dans R. Au fil du temps, je suis tombé sur quelques morceaux de code qui empruntent un chemin différent ici, et je n'ai pas trouvé de position (officielle) correcte sur ce sujet.
Jusqu'à présent, j'ai écrit des arguments optionnels comme celui-ci:
fooBar <- function(x,y=NULL){
if(!is.null(y)) x <- x+y
return(x)
}
fooBar(3) # 3
fooBar(3,1.5) # 4.5
La fonction renvoie simplement son argument si seulement x
est fourni. Il utilise une NULL
valeur par défaut pour le deuxième argument et si cet argument ne l'est pas NULL
, la fonction ajoute les deux nombres.
Alternativement, on pourrait écrire la fonction comme ceci (où le deuxième argument doit être spécifié par son nom, mais on pourrait aussi unlist(z)
ou définir à la z <- sum(...)
place):
fooBar <- function(x,...){
z <- list(...)
if(!is.null(z$y)) x <- x+z$y
return(x)
}
fooBar(3) # 3
fooBar(3,y=1.5) # 4.5
Personnellement, je préfère la première version. Cependant, je peux voir le bien et le mal avec les deux. La première version est un peu moins sujette aux erreurs, mais la seconde pourrait être utilisée pour incorporer un nombre arbitraire d'options.
Existe-t-il une manière "correcte" de spécifier des arguments optionnels dans R? Jusqu'à présent, j'ai choisi la première approche, mais les deux peuvent parfois se sentir un peu "hacky".
xy.coords
voir une approche couramment utilisée.xy.coords
mentionné par Carl Witthoft l peut être trouvé à xy.coordsRéponses:
Vous pouvez également utiliser
missing()
pour tester si l'argument ay
été fourni ou non :la source
missing()
est également plus expressif dans le sens où il "dit ce que cela signifie". De plus, il permet aux utilisateurs de transmettre une valeur NULL, là où cela a du sens!@param x numeric; something something; @param y numeric; **optional** something something; @param z logical; **optional** something something
missing()
est terrible lorsque vous voulez passer des arguments d'une fonction à une autre.Pour être honnête, j'aime la première façon pour l'OP de le démarrer avec une
NULL
valeur, puis de la vérifier avecis.null
(principalement parce que c'est très simple et facile à comprendre). Cela dépend peut-être de la façon dont les gens sont habitués au codage, mais le Hadley semble également le souteniris.null
:Extrait du livre de Hadley "Advanced-R" Chapitre 6, Fonctions, p.84 (pour la version en ligne, vérifiez ici ):
la source
NULL
moyen depuis un bon moment et c'est probablement pour cela que je suis plus habitué quand je vois les codes sources. Cela me semble plus naturel. Cela dit, comme vous dites que la base R adopte les deux approches, cela dépend vraiment des préférences individuelles.is.null
et enmissing
fonction du contexte et de la raison pour laquelle l'argument est utilisé.Voici mes règles de base:
Si les valeurs par défaut peuvent être calculées à partir d'autres paramètres, utilisez des expressions par défaut comme dans:
sinon en utilisant manquant
Dans le cas rare où vous pensez qu'un utilisateur peut vouloir spécifier une valeur par défaut qui dure toute une session R, utilisez
getOption
Si certains paramètres s'appliquent en fonction de la classe du premier argument, utilisez un générique S3:
À utiliser
...
uniquement lorsque vous transmettez des paramètres supplémentaires à une autre fonctionEnfin, si vous choisissez l'utilisation
...
sans passer les points sur une autre fonction, avertissez l'utilisateur que votre fonction ignore tous les paramètres inutilisés car cela peut être très déroutant sinon:la source
NULL
de la signature de fonction de l'OP , car elle est plus pratique pour créer des fonctions qui s'enchaînent bien.Il existe plusieurs options et aucune d'elles n'est la manière officielle correcte et aucune d'elles n'est vraiment incorrecte, bien qu'elles puissent transmettre des informations différentes à l'ordinateur et aux autres lisant votre code.
Pour l'exemple donné, je pense que l'option la plus claire serait de fournir une valeur d'identité par défaut, dans ce cas, faites quelque chose comme:
C'est la plus courte des options présentées jusqu'à présent et la brièveté peut aider à la lisibilité (et parfois même à la vitesse d'exécution). Il est clair que ce qui est retourné est la somme de x et y et vous pouvez voir que y ne reçoit pas une valeur qui sera 0 qui, une fois ajoutée à x, donnera simplement x. Évidemment, si quelque chose de plus compliqué que l'addition est utilisé, une valeur d'identité différente sera nécessaire (s'il en existe une).
Une chose que j'aime vraiment dans cette approche, c'est qu'il est clair quelle est la valeur par défaut lors de l'utilisation de la
args
fonction, ou même en regardant le fichier d'aide (vous n'avez pas besoin de faire défiler les détails, c'est juste là dans l'utilisation ).L'inconvénient de cette méthode est que lorsque la valeur par défaut est complexe (nécessitant plusieurs lignes de code), cela réduirait probablement la lisibilité d'essayer de mettre tout cela dans la valeur par défaut et les approches
missing
ouNULL
deviendraient beaucoup plus raisonnables.Certaines des autres différences entre les méthodes apparaîtront lorsque le paramètre est transmis à une autre fonction ou lors de l'utilisation des fonctions
match.call
ousys.call
.Donc, je suppose que la méthode "correcte" dépend de ce que vous prévoyez de faire avec cet argument particulier et des informations que vous voulez transmettre aux lecteurs de votre code.
la source
J'aurais tendance à préférer utiliser NULL pour la clarté de ce qui est requis et de ce qui est facultatif. Un mot d'avertissement sur l'utilisation des valeurs par défaut qui dépendent d'autres arguments, comme suggéré par Jthorpe. La valeur n'est pas définie lorsque la fonction est appelée, mais lorsque l'argument est référencé pour la première fois! Par exemple:
Par contre, si vous référencez y avant de changer x:
C'est un peu dangereux, car il est difficile de garder une trace de ce que "y" est initialisé comme s'il n'était pas appelé au début de la fonction.
la source
Je voulais juste souligner que la
sink
fonction intégrée a de bons exemples de différentes façons de définir des arguments dans une fonction:la source
que dis-tu de ça?
Puis essayez:
la source