Czy programowanie funkcjonalne jest po prostu inne, czy też jest naprawdę trudniejsze?

12

Czy programowanie funkcjonalne jest po prostu inne , czy też jest naprawdę trudniejsze ?

Powiedz kogoś, kto nigdy wcześniej nie nauczył się programowania i jest nauczony programowania funkcjonalnego. kontra ktoś, kto nigdy wcześniej nie nauczył się programowania i uczy się programowania imperatywnego. co będzie trudniejsze? czy to samo?

Moje pytanie: powiedzmy, że teraz problemem jest wejście do wielbłąda,

taki, który qwe_asd_zxc_rty_fgh_vbnstaje sięqweAsdZxcRtyFghVbn

Procedura polega na:

  1. podzielić to wzdłuż _
  2. zapętlaj tablicę, pomijając pierwszy element
  3. do każdego wpisu dodajemy wielką literę
  4. połącz wyniki razem

Funkcjonalny sposób to:

  1. jeśli nie można znaleźć _zwrotuinput
  2. przeciąć pierwszy inputwzdłuż _(tak, że dostajemy qwei asd_zxc_rty_gfh_cvb)
  3. wielką literą pierwszą literę headi konkatuj to zf(tail)

Ok, jeśli masz zaplecze funkcjonalne ORAZ posiadasz duże doświadczenie w programowaniu procedur, chciałbym zapytać: czy zajmie ci to więcej czasu, aby dowiedzieć się, jak to zrobić, czy też zajmie ci więcej czasu, aby dowiedzieć się, jak to zrobić?

Jeśli posiadasz doświadczenie proceduralne, ale masz wieloletnie doświadczenie w programowaniu funkcjonalnym, chciałbym zadać to samo pytanie: czy zajmie Ci to więcej czasu na opracowanie procedury, czy też zajmie Ci więcej czasu na zrozumienie działania droga?

Pacerier
źródło
5
Ehrm, „sposób proceduralny” wydaje mi się doskonale funkcjonalny, jeśli użyjemy mapkroku 3 zamiast pętli mutacyjnej. Drugie podejście rozważam tylko wtedy, gdy w standardowej bibliotece nie ma funkcji podziału (w takim przypadku należy ją porównać do rozwiązania imperatywnego, które również nie korzysta split).
sepp2k
8
W żadnym z twoich przykładów nie ma nic szczególnie funkcjonalnego ani proceduralnego. Wydaje mi się, że próbujesz ekstrapolować błędne rozumienie programowania funkcjonalnego. Nic dziwnego, że otrzymujesz niezwykłe wyniki.
Rein Henrichs,
Nie sądzę, że programowanie funkcjonalne jest trudne, jest po prostu inne. Jeśli nie masz doświadczenia w programowaniu, uczysz się równie łatwo, ale z jakiegoś powodu po opanowaniu programowania imperatywnego programowanie funkcjonalne staje się trudne.
dan_waterworth
1
Myślę, że rozróżnienie między funkcjonalnym a proceduralnym staje się dyskusyjne, ponieważ języki obsługi takie jak JavaScript, C #, Groovy zawierają coraz więcej funkcji.
user281377,
2
Programowanie imperatywne jest znacznie trudniejsze i sprzeczne z intuicją dla tych, którzy nie mieli wcześniejszego doświadczenia w programowaniu. Takie wyrażenie x=x+1może wysadzić nieoczekiwany mózg. Programowanie funkcjonalne jest naturalne, nie jest niczym więcej niż czystymi i wygodnymi funkcjami ściśle matematycznymi.
SK-logic

Odpowiedzi:

12

Po prostu inny. Programowanie funkcjonalne jest znacznie ściślej związane z matematyką, którą zna większość ludzi. Cała ta „niezmienna zmienna” jest szokiem dla programistów-imperatywów, w których „zmienne” nastawienie jest głęboko zakorzenione.

Dla początkujących jest często dość intuicyjne, że nie można po prostu zmienić wartości czegoś.

Tam, gdzie studiowałem CS, uczyliśmy się języka funkcjonalnego jako naszego pierwszego kursu. I wszyscy, którzy wcześniej nauczyli się C ++ lub Java, mieli z tym problem. Ci, którzy byli nowicjuszami w programowaniu, dość łatwo to zauważyli.

jalf
źródło
Jalf Czy jesteś jednym z nowych programistów i dość łatwo go podjąłeś?
Pacerier
Byłem gdzieś pośrodku. Wcześniej trochę przeszukiwałem C ++ i trochę PHP, ale nie na tyle, żeby naprawdę przyzwyczaić się do trybu myślenia. Wzór był dość wyraźny, gdy spojrzałeś na klasę jako całość. Również, było to prawie dziesięć lat temu, więc nie, nie jestem całkiem nowy w programowaniu dzisiaj;)
jalf
Niezmienne zmienne? Czy matematyka nie jest funkcjonalnym językiem programowania? zmienne w matematyce są z pewnością zmienne, nie?
user56834 18.01.2018
20

Po prostu jest inaczej

Kiedy programujesz, zasadniczo tłumaczysz sposób rozumowania na kod, odległość między twoimi myślami a ostatecznym rozwiązaniem można by nazwać „luką poznawczą”. Im większa szczelina, tym trudniej będzie ją pokonać.

Jeśli pochodzisz z przeszłości proceduralnej, nauczysz się myśleć proceduralnie, więc różnica będzie mniejsza niż w przypadku kodu funkcjonalnego i odwrotnie.

Jedynym sposobem, aby paradygmat programowania był wewnętrznie łatwiejszy niż cokolwiek innego, byłoby zmapowanie go do czegoś, co już znasz, na przykład zwykłego języka, aby zacząć od krótszej luki.

W każdym razie funkcjonalna i proceduralna koncepcja jest dość płynna i często się pokrywają

Homde
źródło
4

Tak, programowanie funkcjonalne może być trudne do zrozumienia dla wielu osób (powiedziałbym, zwłaszcza tym, którzy już wcześniej byli narażeni na programowanie proceduralne).

Powiedziałbym również, że twój przykład programowania funkcjonalnego nie jest naprawdę dobrym przykładem programowania funkcjonalnego. Wykorzystuje rekurencję i po prostu komponuje wynik zamiast modyfikować stan, ale niewiele więcej.

Aby uzyskać lepszy przykład programowania funkcjonalnego, rozważ bardziej ogólny problem: zamiast „szukać podkreślenia i przekonwertować następną literę na wielkie litery”, rozważ to jako jeden szczególny przypadek wyszukiwania wzorca i wykonania dowolnego kodu, gdy zostało znalezione.

Wiele języków obsługuje to, ale aby to zrobić, musimy określić wzorzec jako coś w rodzaju wyrażenia regularnego. Wyrażenia regularne nie są jednak niczym więcej niż językiem programowania specjalnego przeznaczenia, a implementacja RE jest kompilatorem i / lub tłumaczem dla tego języka. Rezultatem kompilacji RE jest w zasadzie funkcja, która wykonuje się (na specjalnej maszynie wirtualnej RE), aby dopasować wyrażenie do niektórych danych wejściowych.

W czymś takim, jak Perl, używasz specjalnego języka do określenia wzorca, specjalnego kompilatora do konwersji tego ciągu na coś w rodzaju funkcji, a także specjalnego interpretera, który bierze tę funkcję do wykonania. W języku funkcjonalnym zwykle używasz samego języka, aby określić wzorzec, a własnego kompilatora używa się do wygenerowania prawdziwej funkcji. Możemy wygenerować tę funkcję w locie (mniej więcej tak, jak możemy skompilować RE, kiedy chcemy), ale kiedy to zrobimy, wynik może być wykonany jak każda inna funkcja w języku zamiast potrzebować specjalnych rzeczy RE w tym celu.

Rezultatem jest to, że można generalizować problemu powyżej stosunkowo łatwo. Zamiast na stałe wpisywać „_” i „wielkie litery” bezpośrednio w transformacji, możemy jednak mieć coś takiego:

s&r(pattern, transform, string) {
    if (!pattern(string))
        return string
    else
        return transform(matched part of string) + s&r(rest of string);
}

Ale w przeciwieństwie do czegoś, w którym określamy wzorzec jako RE, możemy określić wzorzec bezpośrednio jako rzeczywistą funkcję i nadal go używać, coś w stylu:

my_pattern(string) return beginning(string) == '_';

A następnie przekazujemy tę funkcję s & r. W tej chwili jest to dość trywialna funkcja i zakodowaliśmy ją całkowicie statycznie. Język funkcjonalny w dużej mierze staje się interesujący, gdy używamy go tak, jak możemy REs i generujemy całkowicie nową funkcję w locie na podstawie czegoś takiego jak dane wejściowe użytkownika, ale w przeciwieństwie do RE, ta funkcja nie potrzebuje specjalnego interpretera RE - to jest po prostu normalna funkcja jak każda inna.

Jerry Coffin
źródło
4

Oto pełny kod w Racket :

;; camelize : string -> string
(define (camelize str)
  (let ([parts (regexp-split #rx"_" str)])
    ;; result of regexp-split is never empty
    (apply string-append
           (first parts)
           (map string-titlecase (rest parts)))))

(camelize "qwe_asd_zxc_rty_fgh_vbn")
;; => "qweAsdZxcRtyFghVbn"

Jako funkcjonalny programista z doświadczeniem proceduralnym nie sądzę, że zajęłoby mi więcej czasu, aby „wymyślić” rozwiązanie proceduralne, ale z pewnością zajęłoby mi więcej czasu, aby je wpisać.

BTW, przykładowy oczekiwany wynik w oryginalnym poście jest niepoprawny: brakuje mu litery „h” pod koniec.

Ryan Culpepper
źródło
gd za wskazanie tego. edytowane
Pacerier,
3

Moja teoria zwierząt domowych polega na tym, że modele programowania są łatwiejsze do zrozumienia, im bliżej są faktycznej pracy komputerów. Wskaźniki są trudne do zrozumienia, dopóki nie uświadomisz sobie, że są to zasadniczo adresy maszynowe. Trudno jest zrozumieć rekurencję, dopóki świadomie nie przejdziesz małego przykładu, nie zobaczysz ramek stosu i nie zauważysz, gdzie przechowywane są różne wartości tej samej zmiennej. Nie oznacza to, że programowanie asemblera jest łatwiejsze niż programowanie na wysokim poziomie, ale widzenie, jak to się robi, czyni cuda dla modelu mentalnego, który jest kluczem do biegłości - czy to w programowaniu, czy w ogólnej użyteczności.

Teraz model proceduralny jest nieco bliższy zwykłej architekturze maszyny: przypisania są zapisywane w pamięci (lub rejestrze). Wywołania procedur są tak naprawdę tylko fantazyjnymi skokami, w ifrzeczywistości są skokami warunkowymi itp. Ale w Lisp, na przykład, nie ma prostego niskiego poziomu odpowiednika wiązania leksykalnego lub wyrażenia lambda. Zrozumienie tego wymaga wyobrażenia sobie zupełnie odrębnej abstrakcyjnej maszyny funkcjonalnej między poziomem języka a maszyną fizyczną, ponieważ i najwyraźniej większość ludzi nigdy nie dociera tak daleko.

(Ja jestem zaznajomiony z ideą, że architektura von Neumanna jest ostatecznie arbitralne, a my nie powinniśmy umysły początkujących uchybia z taką nieistotnych szczegółów architektury maszyny, a zamiast bezpośrednio wprowadzić je do semantyki języków programowania. W rzeczywistości, mam sam prowadziłem kilka takich kursów. Ale coraz bardziej czuję, że jest to szlachetny, ale mylony cel; ludzie uczą się programowania, budując zrozumienie od podstaw, a droga do programowania funkcjonalnego jest po prostu nieco dłuższa.)

Kilian Foth
źródło
7
Zgodnie z tą logiką asembler powinien być najłatwiejszym do nauczenia się ze wszystkich języków :)
Homde
4
Programowanie funkcjonalne jest dość łatwe do zrozumienia, jeśli podejdziesz do niego z właściwego kierunku. Wspomniana „abstrakcyjna maszyna funkcjonalna” to po prostu algebra . Oceny dokonuje się poprzez przepisywanie terminów; zastosowanie funkcji odbywa się przez podstawienie. Programiści uczą się rozwiązywać problemy za pomocą tych samych narzędzi, które widzieli już od lat klauzul matematycznych. Jeśli ścigają CS, jest wystarczająco dużo czasu, aby później spotkać stos żółwi; jeśli nie, nadal nauczyli się przydatnych umiejętności rozwiązywania problemów i zasad projektowania. Zobacz, jak projektować programy .
Ryan Culpepper,
2
@ mko Nie, według tej logiki rzeczywiste bajty 011011001001101...byłyby najłatwiejszym językiem do nauki!
MarkJ
2
@konrad: Asembler RISC JEST prawdopodobnie najłatwiejszym językiem do nauki. Wiedzieć, jak zrobić z tego coś pożytecznego, to kolejna historia razem ...
Bryan Boettcher