Większość języków głównego nurtu, w tym języki programowania obiektowego (OOP), takie jak C #, Visual Basic, C ++ i Java, zostały zaprojektowane przede wszystkim do obsługi programowania imperatywnego (proceduralnego), podczas gdy języki podobne do Haskell / gofer są czysto funkcjonalne. Czy ktoś może wyjaśnić, jaka jest różnica między tymi dwoma sposobami programowania?
Wiem, że wybór sposobu programowania zależy od wymagań użytkownika, ale dlaczego warto uczyć się języków programowania funkcjonalnego?
oop
functional-programming
Swapnil Kotwal
źródło
źródło
Odpowiedzi:
Definicja: Język imperatywny używa sekwencji instrukcji, aby określić, jak osiągnąć określony cel. Mówi się, że te instrukcje zmieniają stan programu, gdy każda z nich jest wykonywana po kolei.
Przykłady: Java jest językiem imperatywnym. Na przykład można utworzyć program, aby dodać serię liczb:
Każda instrukcja zmienia stan programu, od przypisywania wartości do każdej zmiennej do końcowego dodania tych wartości. Używając sekwencji pięciu instrukcji, program wyraźnie mówi, jak dodać do siebie liczby 5, 10 i 15.
Języki funkcyjne: Paradygmat programowania funkcjonalnego został wyraźnie stworzony w celu wspierania czysto funkcjonalnego podejścia do rozwiązywania problemów. Programowanie funkcyjne jest formą programowania deklaratywnego.
Zalety czystych funkcji: Głównym powodem implementacji transformacji funkcjonalnych jako czystych funkcji jest to, że czyste funkcje są komponowalne, to znaczy niezależne i bezpaństwowe. Cechy te niosą ze sobą szereg korzyści, w tym: Zwiększoną czytelność i łatwość konserwacji. Dzieje się tak, ponieważ każda funkcja jest zaprojektowana do wykonania określonego zadania, biorąc pod uwagę jej argumenty. Funkcja nie jest zależna od żadnego stanu zewnętrznego.
Łatwiejszy powtarzalny rozwój. Ponieważ kod jest łatwiejszy do refaktoryzacji, zmiany w projekcie są często łatwiejsze do wdrożenia. Na przykład załóżmy, że piszesz skomplikowaną transformację, a następnie zdajesz sobie sprawę, że jakiś kod jest powtarzany kilka razy w transformacji. Jeśli dokonasz refaktoryzacji za pomocą czystej metody, możesz dowolnie wywoływać swoją czystą metodę, nie martwiąc się o skutki uboczne.
Łatwiejsze testowanie i debugowanie. Ponieważ czyste funkcje można łatwiej testować w izolacji, można napisać kod testowy, który wywołuje czystą funkcję z typowymi wartościami, prawidłowymi przypadkami brzegowymi i nieprawidłowymi przypadkami brzegowymi.
Dla języków OOP lub imperatywnych:
Języki zorientowane obiektowo są dobre, gdy masz ustalony zestaw operacji na rzeczach, a gdy kod ewoluuje, dodajesz przede wszystkim nowe rzeczy. Można to osiągnąć poprzez dodanie nowych klas, które implementują istniejące metody, a istniejące klasy pozostają w spokoju.
Języki funkcjonalne są dobre, gdy masz ustalony zestaw rzeczy, a gdy twój kod ewoluuje, dodajesz głównie nowe operacje do istniejących rzeczy. Można to osiągnąć, dodając nowe funkcje, które obliczają z istniejącymi typami danych, a istniejące funkcje pozostają w spokoju.
Cons:
Wybór sposobu programowania zależy od wymagań użytkownika, więc szkoda jest tylko wtedy, gdy użytkownik nie wybierze właściwego sposobu.
Kiedy ewolucja idzie w złym kierunku, masz problemy:
źródło
Oto różnica:
Tryb rozkazujący:
... i tak dalej i dalej ...
Deklaratywne, których funkcjonalność jest podkategorią:
... i tak dalej i dalej ...
Podsumowanie: W językach imperatywnych mówisz komputerowi, jak zmieniać bity, bajty i słowa w jego pamięci oraz w jakiej kolejności. W funkcjonalnych informujemy komputer, czym są rzeczy, działania itp. Na przykład mówimy, że silnia 0 to 1, a silnia każdej innej liczby naturalnej jest iloczynem tej liczby i silni jej poprzednika. Nie mówimy: aby obliczyć silnię n, zarezerwuj obszar pamięci i zapisz tam 1, a następnie pomnóż liczbę w tym obszarze przez liczby od 2 do n i zapisz wynik w tym samym miejscu, a na końcu, region pamięci będzie zawierał silnię.
źródło
Większość współczesnych języków jest w różnym stopniu zarówno imperatywna, jak i funkcjonalna, ale aby lepiej zrozumieć programowanie funkcjonalne, najlepiej będzie wziąć przykład czystego języka funkcjonalnego, takiego jak Haskell, w przeciwieństwie do imperatywnego kodu w języku niezbyt funkcjonalnym, takim jak java / c #. Uważam, że zawsze łatwo jest to wyjaśnić na przykładzie, więc poniżej jest jeden.
Programowanie funkcjonalne: oblicz silnię z n ie n! tj. nx (n-1) x (n-2) x ... x 2 X 1
Zauważ, że Haskel pozwala na przeciążanie funkcji do poziomu wartości argumentów. Poniżej znajduje się przykład kodu imperatywnego o rosnącym stopniu imperatywności:
Ten odczyt może być dobrym odniesieniem do zrozumienia, jak imperatywny kod koncentruje się bardziej na tym, jak część, stan maszyny (pętla i in for), kolejność wykonywania, sterowanie przepływem.
Późniejszy przykład można postrzegać jako z grubsza kod java / c # lang, a pierwsza część jako ograniczenie samego języka w przeciwieństwie do Haskella do przeciążania funkcji wartością (zero), a zatem można powiedzieć, że nie jest to purystyczny język funkcjonalny, z drugiej strony ręka można powiedzieć, że obsługuje program funkcjonalny. do pewnego stopnia.
Ujawnienie: żaden z powyższych kodów nie jest testowany / wykonywany, ale mam nadzieję, że powinien być wystarczająco dobry, aby przekazać koncepcję; również byłbym wdzięczny za komentarze do każdej takiej korekty :)
źródło
return n * factorial(n-1);
?n * (n-1)
Programowanie funkcyjne jest formą programowania deklaratywnego, która opisuje logikę obliczeń, a kolejność wykonywania jest całkowicie pomniejszona.
Problem: Chcę zmienić to stworzenie z konia w żyrafę.
Każdy element można uruchomić w dowolnej kolejności, aby uzyskać ten sam wynik.
Programowanie imperatywne jest proceduralne. Stan i porządek są ważne.
Problem: Chcę zaparkować samochód.
Aby osiągnąć pożądany rezultat, należy wykonać każdy krok. Wciągnięcie do garażu, gdy drzwi garażowe są zamknięte, spowodowałoby pęknięcie bramy.
źródło
Programowanie funkcjonalne to „programowanie z funkcjami”, w którym funkcja ma pewne oczekiwane właściwości matematyczne, w tym przezroczystość referencyjną. Z tych właściwości wypływają dalsze właściwości, w szczególności znane kroki rozumowania możliwe dzięki substytucyjności, które prowadzą do dowodów matematycznych (tj. Uzasadniania zaufania do wyniku).
Wynika z tego, że program funkcjonalny jest jedynie wyrażeniem.
Możesz łatwo zobaczyć kontrast między tymi dwoma stylami, odnotowując miejsca w programie imperatywnym, w których wyrażenie nie jest już referencyjnie przezroczyste (a zatem nie jest zbudowane z funkcji i wartości i nie może samo być częścią funkcji). Dwa najbardziej oczywiste miejsca to: mutacja (np. Zmienne) inne skutki uboczne nielokalny przepływ kontroli (np. Wyjątki)
Na tej strukturze programów jako wyrażeń, które składają się z funkcji i wartości, zbudowany jest cały praktyczny paradygmat języków, pojęć, „wzorców funkcjonalnych”, kombinatorów i różnych systemów typów i algorytmów oceny.
Zgodnie z najbardziej ekstremalną definicją, prawie każdy język - nawet C lub Java - można nazwać funkcjonalnym, ale zwykle ludzie rezerwują ten termin dla języków z konkretnie stosownymi abstrakcjami (takimi jak zamknięcia, niezmienne wartości i pomoce składniowe, takie jak dopasowywanie wzorców). Jeśli chodzi o użycie programowania funkcyjnego, to wymaga użycia functinów i buduje kod bez żadnych skutków ubocznych. używany do pisania dowodów
źródło
Imperatywny styl programowania był praktykowany w tworzeniu stron internetowych od 2005 do 2013 roku.
W przypadku programowania imperatywnego napisaliśmy kod, który dokładnie wyszczególniał, co powinna robić nasza aplikacja, krok po kroku.
Funkcjonalny styl programowania tworzy abstrakcję poprzez sprytne sposoby łączenia funkcji.
W odpowiedziach jest wzmianka o programowaniu deklaratywnym, aw związku z tym powiem, że programowanie deklaratywne wymienia pewne zasady, których mamy przestrzegać. Następnie podajemy do naszej aplikacji coś, co nazywamy stanem początkowym, i pozwalamy tym regułom definiować sposób działania aplikacji.
Teraz te krótkie opisy prawdopodobnie nie mają większego sensu, więc przejrzyjmy różnice między programowaniem imperatywnym i deklaratywnym, przechodząc przez analogię.
Wyobraź sobie, że nie tworzymy oprogramowania, ale zamiast tego zarabiamy na życie pieczeniem ciast. Być może jesteśmy złymi piekarzami i nie wiemy, jak upiec pyszne ciasto tak, jak powinniśmy.
Więc nasz szef daje nam listę kierunków, co znamy jako przepis.
Przepis podpowie nam, jak zrobić ciasto. Jeden przepis jest napisany imperatywnym stylem:
Deklaratywny przepis wykonałby następujące czynności:
1 szklanka mąki, 1 jajko, 1 szklanka cukru - stan początkowy
Zasady
Tak więc podejścia imperatywne charakteryzują się podejściem krok po kroku. Rozpoczynasz od kroku pierwszego i przechodzisz do kroku 2 i tak dalej.
W końcu otrzymujesz produkt końcowy. Robiąc to ciasto, bierzemy te składniki, mieszamy je, wkładamy na patelnię i do piekarnika, a otrzymujemy produkt końcowy.
W świecie deklaratywnym jest inaczej. W recepturze deklaratywnej podzielilibyśmy nasz przepis na dwie oddzielne części, zaczynając od jednej części, która zawiera stan początkowy receptury, podobnie jak zmienne. Zatem naszymi zmiennymi są tutaj ilości naszych składników i ich rodzaj.
Bierzemy stan początkowy lub składniki początkowe i stosujemy do nich pewne zasady.
Więc bierzemy stan początkowy i powtarzamy te zasady w kółko, aż będziemy gotowi do spożycia rabarbarowego ciasta truskawkowego lub czegokolwiek innego.
Dlatego w podejściu deklaratywnym musimy wiedzieć, jak prawidłowo ustrukturyzować te reguły.
Więc zasady, które moglibyśmy chcieć zbadać nasze składniki lub stan, jeśli zostaną zmieszane, włóż je do garnka.
Z naszym stanem początkowym to nie pasuje, ponieważ nie wymieszaliśmy jeszcze naszych składników.
Tak więc zasada 2 mówi, że jeśli się nie zmieszają, wymieszaj je w misce. Okay yeah ta zasada obowiązuje.
Teraz mamy miskę mieszanych składników jako nasz stan.
Teraz ponownie stosujemy ten nowy stan do naszych zasad.
Więc zasada 1 mówi, że jeśli składniki są wymieszane, umieść je na patelni, ok, tak, teraz obowiązuje zasada 1, zróbmy to.
Teraz mamy ten nowy stan, w którym składniki są mieszane i na patelni. Zasada 1 nie ma już zastosowania, zasada 2 nie ma zastosowania.
Zasada 3 mówi, że jeśli składniki są na patelni, umieść je w piekarniku, świetnie, że zasada dotyczy tego nowego stanu, zróbmy to.
I kończymy na pysznej gorącej szarlotce lub czymkolwiek.
Teraz, jeśli jesteś podobny do mnie, możesz pomyśleć, dlaczego nadal nie robimy programowania imperatywnego. To ma sens.
Cóż, w przypadku prostych przepływów tak, ale większość aplikacji internetowych ma bardziej złożone przepływy, których nie można prawidłowo uchwycić za pomocą imperatywnego projektowania programowania.
W podejściu deklaratywnym możemy mieć pewne składniki początkowe lub stan początkowy, np.
textInput=“”
Pojedynczą zmienną.Może tekst zaczyna się od pustego ciągu.
Bierzemy ten stan początkowy i stosujemy go do zestawu reguł zdefiniowanych w Twojej aplikacji.
Jeśli użytkownik wprowadzi tekst, zaktualizuj wprowadzanie tekstu. Cóż, teraz to nie ma zastosowania.
Jeśli szablon jest renderowany, oblicz widget.
Cóż, nic z tego nie ma zastosowania, więc program po prostu zaczeka na jakieś wydarzenie.
Tak więc w pewnym momencie użytkownik aktualizuje wprowadzany tekst, a następnie możemy zastosować regułę numer 1.
Możemy to zaktualizować do
“abcd”
Więc właśnie zaktualizowaliśmy nasze aktualizacje text i textInput, reguła numer 2 nie ma zastosowania, reguła numer 3 mówi, że jeśli wprowadzanie tekstu to aktualizacja, która właśnie nastąpiła, to ponownie renderuj szablon, a następnie wracamy do reguły 2, która mówi, że szablon jest renderowany , obliczyć widget, w porządku, obliczmy widget.
Ogólnie rzecz biorąc, jako programiści, chcemy dążyć do bardziej deklaratywnych projektów programistycznych.
Imperatyw wydaje się bardziej jasny i oczywisty, ale podejście deklaratywne daje się bardzo dobrze skalować w przypadku większych aplikacji.
źródło
• Języki imperatywne:
Efektywne wykonanie
Złożona semantyka
Złożona składnia
Współbieżność jest zaprojektowana przez programistę
Złożone testy, bez referencyjnej przejrzystości, mają skutki uboczne
• Języki funkcjonalne:
Prosta semantyka
Prosta składnia
Mniej wydajne wykonanie
Programy mogą być automatycznie współbieżne
Proste testowanie, referencyjna przejrzystość, nie ma skutków ubocznych
źródło
Myślę, że można wyrazić programowanie funkcjonalne w sposób imperatywny:
if... else
/switch
instrukcjiZ takim podejściem są ogromne problemy:
Programowanie funkcjonalne, traktujące funkcje / metody jak obiekty i uwzględniające bezpaństwowość, narodziło się, aby rozwiązać te problemy, które moim zdaniem.
Przykładowe zastosowania: aplikacje frontendowe, takie jak Android, iOS lub logika aplikacji internetowych, w tym. komunikacja z backendem.
Inne wyzwania podczas symulacji programowania funkcjonalnego z kodem imperatywnym / proceduralnym:
Wierzę również, że pod koniec dnia kod funkcjonalny zostanie przetłumaczony na kod asemblerowy lub maszynowy, co jest konieczne / proceduralne dla kompilatorów. Jednak o ile nie piszesz asemblera, ponieważ ludzie piszą kod w języku wysokiego poziomu / czytelnym dla człowieka, programowanie funkcjonalne jest bardziej odpowiednim sposobem wyrażenia dla wymienionych scenariuszy
źródło
Wiem, że to pytanie jest starsze i inni już je dobrze wyjaśnili, chciałbym podać przykładowy problem, który wyjaśnia to samo w prostych słowach.
Problem: Zapisywanie tabeli jedynek.
Rozwiązanie: -
Według stylu rozkazującego: =>
Według stylu funkcjonalnego: =>
Wyjaśnienie w stylu imperatywnym instrukcje piszemy bardziej jawnie i które można wywołać w bardziej uproszczony sposób.
W przypadku stylu funkcjonalnego rzeczy, które nie wymagają wyjaśnień, zostaną zignorowane.
źródło