Ceci est une discussion intéressante. Je pense que l'exemple de @ flodel est excellent. Cependant, je pense que cela illustre mon argument (et @koshke le mentionne dans un commentaire) qui a du return
sens lorsque vous utilisez un impératif au lieu d'un style de codage fonctionnel .
Sans trop insister, mais j'aurais réécrit foo
comme ceci:
foo = function() ifelse(a,a,b)
Un style fonctionnel évite les changements d'état, comme le stockage de la valeur de output
. Dans ce style, return
n'est pas à sa place; foo
ressemble plus à une fonction mathématique.
Je suis d'accord avec @flodel: utiliser un système complexe de variables booléennes dans bar
serait moins clair et inutile quand vous l'avez return
. Ce qui rend bar
si accessible aux return
déclarations, c'est qu'il est écrit dans un style impératif. En effet, les variables booléennes représentent les changements "d'état" évités dans un style fonctionnel.
Il est vraiment difficile de réécrire bar
dans un style fonctionnel, car il ne s'agit que de pseudocode, mais l'idée est quelque chose comme ceci:
e_func <- function() do_stuff
d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3)
b_func <- function() {
do_stuff
ifelse(c,1,sapply(seq(b),d_func))
}
bar <- function () {
do_stuff
sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea.
}
La while
boucle serait la plus difficile à réécrire, car elle est contrôlée par les changements d'état de a
.
La perte de vitesse causée par un appel à return
est négligeable, mais l'efficacité obtenue en évitant return
et en réécrivant dans un style fonctionnel est souvent énorme. Dire aux nouveaux utilisateurs d'arrêter d'utiliser return
ne sera probablement pas utile, mais les guider vers un style fonctionnel sera payant.
@Paul return
est nécessaire dans un style impératif car vous voulez souvent quitter la fonction à différents points d'une boucle. Un style fonctionnel n'utilise pas de boucles et n'a donc pas besoin return
. Dans un style purement fonctionnel, l'appel final est presque toujours la valeur de retour souhaitée.
En Python, les fonctions nécessitent une return
instruction. Cependant, si vous avez programmé votre fonction dans un style fonctionnel, vous n'aurez probablement qu'une seule return
instruction: à la fin de votre fonction.
En utilisant un exemple d'un autre article StackOverflow, disons que nous voulions une fonction qui retournait TRUE
si toutes les valeurs d'une donnée x
avaient une longueur impaire. Nous pourrions utiliser deux styles:
# Procedural / Imperative
allOdd = function(x) {
for (i in x) if (length(i) %% 2 == 0) return (FALSE)
return (TRUE)
}
# Functional
allOdd = function(x)
all(length(x) %% 2 == 1)
Dans un style fonctionnel, la valeur à renvoyer tombe naturellement aux extrémités de la fonction. Encore une fois, cela ressemble plus à une fonction mathématique.
@GSee Les avertissements décrits dans ?ifelse
sont certainement intéressants, mais je ne pense pas qu'ils essaient de dissuader l'utilisation de la fonction. En fait, ifelse
a l'avantage de vectoriser automatiquement les fonctions. Par exemple, considérons une version légèrement modifiée de foo
:
foo = function(a) { # Note that it now has an argument
if(a) {
return(a)
} else {
return(b)
}
}
Cette fonction fonctionne bien quand length(a)
est 1. Mais si vous avez réécrit foo
avec unifelse
foo = function (a) ifelse(a,a,b)
Fonctionne maintenant foo
sur n'importe quelle longueur de a
. En fait, cela fonctionnerait même quand a
est une matrice. Renvoyer une valeur de la même forme qu'une test
fonctionnalité qui aide à la vectorisation, pas un problème.
return
est inutile même dans le dernier exemple. La suppressionreturn
peut accélérer un peu, mais à mon avis, c'est parce que R est dit être un langage de programmation fonctionnel.