Co w programowaniu funkcjonalnym robi różnicę?
Programowanie funkcjonalne jest z zasady deklaratywne . Mówisz, jaki jest twój wynik, a nie jak go obliczyć.
Rzućmy okiem na naprawdę funkcjonalną implementację twojego fragmentu kodu. W Haskell byłoby to:
predsum pred numbers = sum (filter pred numbers)
Czy jasne jest, jaki jest wynik? Jest to suma liczb spełniających orzeczenie. Jak to jest obliczane? Nie obchodzi mnie to, zapytaj kompilatora.
Można powiedzieć, że używanie sum
i filter
jest podstępem i nie ma znaczenia. Pozwól to wdrożyć bez tych pomocników (chociaż najlepszym sposobem byłoby ich wdrożenie w pierwszej kolejności).
Rozwiązanie „Functional Programming 101”, które nie korzysta sum
z rekurencji:
sum pred list =
case list of
[] -> 0
h:t -> if pred h then h + sum pred t
else sum pred t
Nadal jest całkiem jasne, jaki jest wynik pod względem wywołania jednej funkcji. Jest albo 0
, albo recursive call + h or 0
, w zależności od pred h
. Nadal dość proste, nawet jeśli wynik końcowy nie jest od razu oczywisty (choć przy odrobinie praktyki to naprawdę brzmi jak for
pętla).
Porównaj to ze swoją wersją:
public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
int result = 0;
foreach(var item in numbers)
if (predicate(item)) result += item;
return result;
}
Jaki jest wynik? O, widzę: pojedynczy return
rachunek, nie zaskakuje tutaj: return result
.
Ale co to jest result
? int result = 0
? To nie wydaje się właściwe. Zrobisz coś później 0
. Ok, dodajesz item
s. I tak dalej.
Oczywiście dla większości programistów jest to dość oczywiste, co dzieje się w takiej prostej funkcji, ale dodaj jakieś dodatkowe return
stwierdzenie i nagle trudniej będzie je wyśledzić. Cały kod dotyczy tego , jak i co pozostało czytelnikowi, aby się zorientować - jest to zdecydowanie bardzo imperatywny styl .
Czy zmienne i pętle są nieprawidłowe?
Nie.
Istnieje wiele rzeczy, które są o wiele łatwiejsze do wyjaśnienia, i wiele algorytmów, które wymagają zmiennego stanu, aby były szybkie. Ale zmienne są z natury bezwzględnie konieczne, wyjaśniając, jak zamiast tego , i dając niewielkie przewidywanie, jaka może być ich wartość, kilka linii później lub po kilku iteracjach pętli. Pętle generalnie wymagają, aby państwo miało sens, dlatego też są z natury bezwzględnie konieczne.
Zmienne i pętle nie są po prostu programowaniem funkcjonalnym.
Podsumowanie
Współczesne programowanie funkcyjne to nieco więcej stylu i użyteczny sposób myślenia niż paradygmat. W tym sposobie myślenia zdecydowanie preferowane są czyste funkcje, ale tak naprawdę to tylko niewielka część.
Większość rozpowszechnionych języków pozwala korzystać z niektórych funkcjonalnych konstrukcji. Na przykład w Pythonie możesz wybierać między:
result = 0
for num in numbers:
if pred(result):
result += num
return result
lub
return sum(filter(pred, numbers))
lub
return sum(n for n in numbers if pred(n))
Te wyrażenia funkcjonalne dobrze pasują do tego rodzaju problemów i po prostu skracają kod (a krótszy jest dobry ). Nie powinieneś bezmyślnie zastępować ich imperatywnym kodem, ale gdy pasują, prawie zawsze są lepszym wyborem.
item
zmienna zmutowana w pętli.