To ciekawa dyskusja. Myślę, że przykład @ flodel jest doskonały. Myślę jednak, że to ilustruje mój punkt (i @koshke wspomina o tym w komentarzu), który return
ma sens, gdy używasz trybu rozkazującego zamiast funkcjonalnego stylu kodowania .
Nie chcę się nad tym zastanawiać, ale napisałbym foo
tak:
foo = function() ifelse(a,a,b)
Funkcjonalny styl pozwala uniknąć zmian stanu, takich jak przechowywanie wartości output
. W tym stylu return
jest nie na miejscu; foo
wygląda bardziej jak funkcja matematyczna.
Zgadzam się z @flodel: użycie skomplikowanego systemu zmiennych boolowskich w bar
byłoby mniej jasne i bezcelowe, jeśli masz return
. To, co czyni bar
tak podatnymi na return
oświadczenia, polega na tym, że jest napisane w trybie rozkazującym. Rzeczywiście, zmienne logiczne reprezentują zmiany „stanu”, których uniknięto w stylu funkcjonalnym.
Naprawdę trudno jest przepisać bar
w funkcjonalnym stylu, ponieważ jest to tylko pseudokod, ale pomysł jest mniej więcej taki:
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.
}
while
Pętla byłaby najtrudniejsza przepisać, ponieważ jest kontrolowane przez państwo do zmian a
.
Utrata prędkości spowodowana wywołaniem return
jest znikoma, ale wydajność uzyskana dzięki unikaniu return
i przepisywaniu w funkcjonalnym stylu jest często ogromna. Powiedzenie nowym użytkownikom, aby przestali używać, return
prawdopodobnie nie pomoże, ale wprowadzenie ich do funkcjonalnego stylu przyniesie korzyści.
@Paul return
jest konieczny w trybie rozkazującym, ponieważ często chcesz wyjść z funkcji w różnych punktach pętli. Funkcjonalny styl nie używa pętli, a zatem nie potrzebuje return
. W czysto funkcjonalnym stylu ostateczne wywołanie jest prawie zawsze pożądaną wartością zwrotną.
W Pythonie funkcje wymagają return
instrukcji. Jeśli jednak zaprogramowałeś swoją funkcję w stylu funkcjonalnym, prawdopodobnie będziesz mieć tylko jedną return
instrukcję: na końcu swojej funkcji.
Korzystając z przykładu z innego postu StackOverflow, powiedzmy, że potrzebujemy funkcji, która zwróci, TRUE
jeśli wszystkie wartości w danym odcinku będą x
nieparzyste. Możemy użyć dwóch stylów:
# 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)
W stylu funkcjonalnym zwracana wartość naturalnie spada na końcu funkcji. Ponownie wygląda bardziej jak funkcja matematyczna.
@GSee Przedstawione ostrzeżenia ?ifelse
są zdecydowanie interesujące, ale nie sądzę, aby próbowały zniechęcić do korzystania z tej funkcji. W rzeczywistości ifelse
ma tę zaletę, że automatycznie wektoryzuje funkcje. Na przykład rozważ nieco zmodyfikowaną wersję foo
:
foo = function(a) { # Note that it now has an argument
if(a) {
return(a)
} else {
return(b)
}
}
Ta funkcja działa dobrze, gdy length(a)
jest 1, ale jeśli przepisałeś ponownie foo
za pomocąifelse
foo = function (a) ifelse(a,a,b)
Teraz foo
działa na dowolnej długości a
. W rzeczywistości zadziałałoby nawet, gdy a
jest matrycą. Zwracanie wartości tego samego kształtu co test
funkcja ułatwiająca wektoryzację, a nie problem.
return
jest niepotrzebny nawet w ostatnim przykładzie. Usunięciereturn
może trochę przyspieszyć, ale moim zdaniem dzieje się tak, ponieważ mówi się, że R jest funkcjonalnym językiem programowania.