A beginner friendly intro to functional programming
Functional programming is a highly valued approach to writing code, and it’s popularity is continuously increasing in aplikacje komercyjne. Programowanie funkcyjne jest paradygmatem pisania kodu i jest wymownie umieszczone we wstępie tego artykułu Wikipedii:
” — styl budowania struktury i elementów programów komputerowych-który traktuje obliczenia jako ocenę funkcji matematycznych i unika zmian stanu i zmiennych danych. Jest to paradygmat programowania deklaratywnego, co oznacza, że programowanie odbywa się za pomocą wyrażeń lub deklaracji zamiast oświadczeń.”
wkrótce zajmiemy się tym, co to dokładnie oznacza, ale najpierw pozbądźmy się podstaw, podając kontekst. Ten artykuł ma na celu, miejmy nadzieję, wprowadzenie podstawowych pojęć programowania funkcyjnego do Ciebie i na koniec, chcielibyśmy ustalić podstawowe zrozumienie, dlaczego zdecydowaliśmy się zastosować go do naszych stylów kodowania, jak to odnosi się do JavaScript i kilka podstawowych wzorców, które można rozpocząć wdrażanie do kodu. Poszukaj drugiego artykułu, który będzie bazował na tym i zagłęb się jeszcze głębiej w omawiane tu pomysły.
korzenie programowania funkcyjnego leżą w rachunku Lambda, który był systemem opracowanym w 1930 roku do wyrażania obliczeń za pomocą funkcji podobnych do tego, co zobaczymy wkrótce. Przez pewien czas koncepcje i języki programowania funkcjonalnego były szeroko omawiane w środowiskach akademickich i naukowych, a ostatecznie doprowadziły do rozwoju komercyjnego oprogramowania. To, co sprawia, że język programowania jest funkcjonalny lub nie, to jego zdolność do ułatwiania paradygmatu programowania funkcyjnego, takimi językami są Ruby, Python, Julia i JavaScript, z których drugi ma właściwości paradygmatów imperatywnych, obiektowych i funkcjonalnych.
spójrzmy na przykład użycia imperatywu, a następnie funkcjonalnego podejścia do wykonania pojedynczego zadania.
const arr = function getOdds(arr){
let odds = ; for(let i = 0; i < arr.length + 1; i++){
if ( i % 2 !== 0 ){
odds.push( i )
};
};
return odds
};console.log(getOdds(arr))
// logs
:
function getOdds2(arr){
return arr.filter(num => num % 2 !== 0)
}console.log(getOdds2(arr))
// logs // this can be even shorter
const getOdds3 = arr => arr.filter(num => num % 2 !== 0)console.log(getOdds3(arr))
// logs
oprócz oczywistych ulepszeń, które sprawia, że podejście funkcjonalne, takie jak krótszy kod, oszczędzając nam kilka naciśnięć klawiszy, a także fakt, że jest znacznie łatwiejszy do zrozumienia na pierwszy rzut oka, jest również elegancki, co skutkuje czystszym kodem, upraszcza debugowanie, testowanie i konserwację.
omówmy to trochę, abyśmy mogli zrozumieć różnice między tymi dwoma podejściami i zdefiniować, czym jest programowanie funkcyjne.
w pierwszym podejściu imperatywnie opisujemy każde zdarzenie, które musi mieć miejsce, abyśmy mogli wykonać zadanie filtrowania liczb nieparzystych z tablicy. Podajemy funkcję tablicę, wewnątrz funkcji tworzymy pustą tablicę, w której będziemy przechowywać nieparzyste liczby. Mówimy, aby zapętliła się nad tablicą, a następnie deklarujemy warunek, jeśli bieżący indeks jest liczbą nieparzystą, wciskamy go do pustej. Pętla for Nie zawiera ostatniej liczby w tablicy, zawiera tylko liczby prowadzące do niej, więc jeśli nasza ostatnia liczba jest nieparzysta, nie zostanie uwzględniona. Dlatego dodajemy +1 podczas definiowania ilości iteracji, które nasza pętla musi wykonać.
w drugim podejściu po prostu definiujemy wynik, który chcemy zobaczyć za pomocą metody zwanej filtrem i pozwalamy maszynie zająć się wszystkimi krokami pomiędzy. Jest to podejście bardziej deklaratywne. Deklarujemy, jaki musi być efekt końcowy, a my zajmiemy się resztą.
teraz możemy zobaczyć, co powyższa definicja stara się wyrazić. Sposób programowania, przy użyciu deklaracji i wyrażeń, a nie instrukcji. Definicja ta otarła jedynie powierzchnię programowania funkcyjnego i będziemy stale opierać się na naszej definicji w miarę wzrostu naszego zrozumienia.
pewne różnice między podejściem iteracyjnym i funkcjonalnym.
- stylistycznie definiujemy problem i zmiany, które chcielibyśmy zobaczyć, a nie wyjaśniamy go krok po kroku.
- nie musimy zarządzać stanem w naszych funkcjach jak powyżej, gdzie zarządzaliśmy stanem naszej pustej tablicy.
- nie musimy tak bardzo martwić się o kolejność wykonania.
- używamy mniej pętli, warunków i używamy więcej wbudowanych metod, funkcji wielokrotnego użytku i rekurencji.
używając tego podejścia, ostatecznie piszemy czystszy kod, który jak wspomniano powyżej, pomaga nam w debugowaniu i konserwacji. Zachowuje modułowość, a sekcje naszego kodu są podzielone na mniejsze części, które możemy z łatwością przetestować. Możemy ponownie używać części naszego kodu za pomocą funkcji pomocniczych i wreszcie, jest to bardziej wydajne, matematyczne podejście do pisania kodu.
filary: Nasz zestaw narzędzi do kodowania do pisania kodu funkcjonalnego
poniżej znajduje się kilka ważnych aspektów pisania kodu funkcjonalnego wysokiej jakości. Widząc, że jest to przegląd, nie będziemy zanurkować zbytnio w każdym z nich, zamiast tego zdefiniujemy tylko kilka kluczowych cech i jeden przykład każdego z nich.
czyste funkcje:
czyste funkcje zwracają wartość wyłącznie na podstawie tego, co zostało do niej przekazane. Chodzi o to, że będzie to czysta funkcja, jeśli zawsze zwróci ten sam wynik, jeśli te same wartości zostaną do niej przekazane, i nie modyfikuje wartości poza zakresem ts, co czyni ją niezależną od jakiegokolwiek stanu w systemie. Dlatego czysta funkcja nigdy nie może mutować danych, nie wywołuje skutków ubocznych i może być łatwo ponownie wykorzystana. Przykładem funkcji nie czystej będzie funkcja, która wywołuje wywołanie API lub zwraca nieprzewidywalny wynik.
prosta nieczysta funkcja:
var tip = 0;function calculateTip( mealTotal ) {
tip = 0.15 * mealTotal;
}calculateTip( 150 )
console.log(tip)
prosta czysta funkcja:
function isPure(x,y) {
return x * y
}console.log(isPure(3,5));
czysta funkcja zwróci dokładny wynik za każdym razem i nie zmutuje żadnych danych poza nią. Ważną rzeczą, na którą należy zwrócić uwagę, jest to, że czysta funkcja musi trzymać się z dala od mutowania danych, więc bądź ostrożny przy wyborze metod, których chcesz użyć w swoich czystych funkcjach. Na przykład, jeśli chcesz scalić dwie tablice wewnątrz swojej funkcji, jak to zwykle robi się w reactowym reduktorze, unikaj używania tablicy.prototyp.metoda push (). Zamiast tego powinieneś użyć Array.prototyp.concat( ), która zachowa stan starych tablic i zwróci nową do użytku.
funkcje wyższego rzędu:
w Javascript funkcje są traktowane jako obiekty, dlatego możemy przekazywać funkcje tak, jak każdą inną wartość. Funkcja wyższego rzędu jest po prostu funkcją, która działa na innych funkcjach. Mogą przyjmować funkcję jako wejście lub zwracać jako wyjście. Zobaczymy prosty przykład w poniższej demonstracji.
funkcje anonimowe:
funkcje anonimowe są bardzo pomocne, gdy potrzebujemy zdefiniować logikę ad hoc tak, jak potrzebujemy. Jak sama nazwa wskazuje, funkcja anonimowa w funkcji bezimiennej, a najczęściej spotykana jako argument do funkcji jako zamiennik funkcji nazwanej, funkcji przypisanej do zmiennej lub zwracanej jako funkcja w funkcji wyższego rzędu. Jego korzenie leżą silnie w rachunku Lambda i są bardzo ważne dla wszystkich funkcjonalnych języków programowania.
anonimowa funkcja przypisana do zmiennej. Łatwe do przekazania i wywołania w razie potrzeby.
const myVar = function(){console.log(‘Anonymous function here!’)}myVar()
funkcja anonimowa jako argument
setInterval(function(){console.log(new Date().getTime())}, 1000);
funkcje anonimowe w ramach funkcji wyższego rzędu
function mealCall(meal){
return function(message){
return console.log(message + " " + meal + ‘!!’)
}
}const announceDinner = mealCall(‘dinner’)
const announceLunch = mealCall(‘breakfast’)announceDinner(‘hey!, come and get your’)
announceLunch(‘Rise and shine! time for’)
Rekurencja
Rekurencja jest dobrze znaną techniką programowania funkcyjnego. Funkcja rekurencyjna jest po prostu funkcją wywołującą samą siebie, a zatem działa jako pętla wykonująca ten sam kod wielokrotnie. Chodzi jednak o jedno zastrzeżenie, należy uważać, aby uniknąć nieskończonych rekursji. Dlatego potrzebna jest podstawa, aby powiedzieć jej, kiedy ma się zatrzymać. Funkcje lub algorytmy rekurencyjne mogą być implementowane zarówno za pomocą rekurencji, jak i pętli, ale lepiej jest użyć rekurencji. Poniżej znajduje się dobrze znany algorytm do faktoryzacji liczby zarówno za pomocą rekurencji, jak i pętli.
funkcja rekurencyjna
function factorialize(num){
if (num === 0 || num === 1){return 1;}
return (num * factorialize(num — 1));
}var result = factorialize(14);console.log(result);
Rekurencja przy użyciu pętli
function factorialize(num) {
if (num === 0 || num === 1){
return 1;
}
for (var i = num-1; i >= 1; i-- ) {
num *= i;
}
return num;
}
console.log(factorialize(6));
omówiliśmy kilka podstawowych składników do pisania kodu funkcjonalnego. Nie zawsze jest możliwe pisanie funkcjonalnego kodu w aplikacji, ale korzyści są duże, gdy są używane w jak największym stopniu. Frameworki takie jak Angular i React są pod silnym wpływem programowania funkcjonalnego, a biblioteki takie jak Redux i Lodash pozwalają nam wykorzystać zalety tego oprogramowania, więc zdecydowanie istnieje duża korzyść, aby zacząć myśleć w sposób FP.
zasoby do nauki więcej? Oto kilka świetnych artykułów i zasobów do dalszej nauki.
- po co uczyć się programowania funkcjonalnego w JavaScript? by Eric Elliot
- Eloquent JavaScript by Marijn Haverbeke
- więc chcesz być programistą funkcjonalnym Charles Scalfani
mam nadzieję, że było to pomocne i w następnym artykule zanurkujemy trochę głębiej i rozpakujemy jeszcze bardziej interesujące i pomocne narzędzia, takie jak metody JavaScript, częściowa aplikacja, currying, Wiązanie argumentów i inne.
możesz klaskać, dzielić się, krytykować lub dzielić się przemyśleniami na ten temat!