Nauczyłem się programować przede wszystkim z punktu widzenia OOP (jak większość z nas, jestem pewien), ale spędziłem dużo czasu próbując nauczyć się, jak rozwiązywać problemy w sposób funkcjonalny. Dobrze rozumiem, jak rozwiązywać problemy obliczeniowe z FP, ale jeśli chodzi o bardziej skomplikowane problemy, zawsze wracam do potrzeb zmiennych obiektów. Na przykład, jeśli piszę symulator cząstek, chcę zaktualizować „obiekty” cząstek ze zmienną pozycją. W jaki sposób z natury „stanowe” problemy są zazwyczaj rozwiązywane za pomocą funkcjonalnych technik programowania?
functional-programming
Andrew Martin
źródło
źródło
Odpowiedzi:
Programy funkcjonalne bardzo dobrze radzą sobie ze stanem, ale wymagają innego spojrzenia na to. Na przykład w przypadku pozycji należy wziąć pod uwagę fakt, że pozycja jest funkcją czasu zamiast stałej wartości . Działa to dobrze w przypadku cząstek podążających ustaloną ścieżką matematyczną, ale wymagana jest inna strategia radzenia sobie ze zmianą ścieżki, na przykład po zderzeniu.
Podstawową strategią jest tutaj tworzenie funkcji, które przyjmują stan i zwracają nowy stan . Tak więc symulator cząstek byłby funkcją, która pobiera
Set
cząstkę na wejściu i zwraca nowąSet
cząstkę po pewnym czasie. Następnie po prostu wielokrotnie wywołujesz tę funkcję, gdy jej wejście jest ustawione na poprzedni wynik.źródło
Jak zauważył @KarlBielefeldt, funkcjonalnym podejściem do takiego problemu jest postrzeganie go jako powrotu do nowego stanu z poprzedniego stanu. Same funkcje nie przechowują żadnych informacji, dlatego zawsze aktualizują stan m do stanu n .
Myślę, że uważasz to za nieskuteczne, ponieważ zakładasz, że poprzedni stan musi być przechowywany w pamięci podczas obliczania nowego stanu. Zauważ, że wybór pomiędzy pisaniem całkowicie nowego stanu a ponownym pisaniem starego na miejscu jest szczegółem implementacji z punktu widzenia funkcjonalnego języka.
Powiedzmy na przykład, że mam listę milionów liczb całkowitych i chcę zwiększyć dziesiątą o jedną jednostkę. Kopiowanie całej listy z nowym numerem na dziesiątej pozycji jest marnotrawstwem, masz rację; ale jest to tylko koncepcyjny sposób opisania operacji kompilatorowi języka lub tłumaczowi. Kompilator lub interpreter może pobrać pierwszą listę i po prostu nadpisać dziesiątą pozycję.
Zaletą opisania operacji w ten sposób jest to, że kompilator może uzasadnić sytuację, gdy wiele wątków chce zaktualizować tę samą listę w różnych pozycjach. Jeśli operacja jest opisana jako „przejdź do tej pozycji i nadpisz to, co znalazłeś”, to programista, a nie kompilator, jest odpowiedzialny za to, aby nadpisania nie kolidowały.
Biorąc to wszystko pod uwagę, nawet w Haskell istnieje monada stanowa, która pomaga modelować sytuacje, w których „utrzymywanie stanu” jest bardziej intuicyjnym rozwiązaniem problemu. Ale proszę również zauważyć, że niektóre problemy, które uważasz za „z natury stanowe, jak pisanie do bazy danych ” mają niezmienne rozwiązania, takie jak Datomic . Może to być zaskakujące, dopóki nie zrozumiesz, że jest to koncepcja, niekoniecznie jej realizacja.
źródło
Subskrybowanie właściwego modelu mentalnego pomaga lepiej myśleć i zarządzać stanem. Moim zdaniem najlepszym modelem mentalnym jest flipbook . Po kliknięciu zrozumiesz, że FP mocno opiera się na trwałych strukturach danych, które przechwytują stan świata i że funkcje są używane do przejścia tego stanu bez jakiejkolwiek mutacji.
Rich Hickey przedstawia te pomysły:
Są inne rozmowy, ale to powinno skierować cię we właściwym kierunku.
źródło
Pisząc duże i umiarkowanie duże aplikacje, często uważałem za użyteczne rozróżnienie między częściami aplikacji, które są stanowe, a tymi, które są bezstanowe.
Klasy / struktury danych w sekcji stanowej przechowują dane aplikacji, a funkcje w tej sekcji działają z niejawną wiedzą o danych aplikacji.
Klasy / struktury danych / funkcje w sekcji bezstanowej służą do obsługi czysto algorytmicznych aspektów aplikacji. Nie mają niejawnej wiedzy na temat danych aplikacji. Działają w charakterze czysto funkcjonalnym. W stanowych częściach aplikacji może wystąpić zmiana stanu jako efekt uboczny uruchamiania funkcji w bezstanowej sekcji aplikacji.
Najtrudniejsze jest ustalenie, które klasy / funkcje należy umieścić w sekcji bezstanowej, a które klasy / funkcje, które należy umieścić w sekcji stanowej, oraz dyscypliny, aby umieścić je w osobnych plikach / bibliotekach.
źródło