Java. Programowanie funkcyjne - ebook
Java. Programowanie funkcyjne - ebook
Większość programistów pracuje zgodnie z paradygmatem programowania imperatywnego, który polega na tworzeniu ciągu instrukcji zmieniających stan programu. Najpoważniejszą wadą tej metody pracy jest podatność kodu na błędy, które trudno jest później wykryć i usunąć. Alternatywą jest programowanie funkcyjne — metodyka, która kładzie największy nacisk na stałe i funkcje. Takie programowanie polega na konstruowaniu funkcji oraz na obliczaniu wartości wyrażeń. W ten sposób otrzymuje się kod odporny na błędy. Niestety, nie zawsze można skorzystać z języków do programowania funkcyjnego.
Niniejsza książka stanowi znakomite wprowadzenie do programowania funkcyjnego na przykładzie Javy. Przedstawiono tu zasady programowania funkcyjnego i metody budowania funkcyjnych struktur danych. Poprzez poznanie paradygmatu funkcyjnego możliwe staje się pisanie lepszych programów, a tworzony kod zawiera mniej błędów i staje się zdecydowanie bardziej niezawodny. W każdym rozdziale znalazły się przykłady kodu, a także ćwiczenia, instrukcje i wskazówki, dzięki którym opanowanie poszczególnych koncepcji stanie się o wiele łatwiejsze. Wyczerpująco omówiono tu m.in. transparentność referencyjną, niezmienność, trwałość i leniwe obliczanie wartości.
Najważniejsze zagadnienia:
- sterowanie wykonaniem programu
- różne rodzaje funkcji w Javie
- rekurencja i jej różne zastosowania
- operacje wejścia-wyjścia
- obsługa błędów
- Java 8 a programowanie funkcyjne
Programowanie funkcyjne — pisz kod funkcjonalny!
Pierre-Yves Saumont jest doświadczonym programistą Javy. Od trzydziestu lat tworzy oprogramowanie wykorzystywane w przedsiębiorstwach. Jest inżynierem do spraw badań i rozwoju w firmie Alcatel-Lucent Submarine Networks. W 1999 r. napisał pierwszą francuskojęzyczną książkę traktującą o programowaniu w Javie (Le guide du developpeur Java).
Spis treści
Przedmowa (13)
Podziękowania (17)
O książce (19)
Rozdział 1. Czym jest programowanie funkcyjne? (23)
- 1.1. Czym jest programowanie funkcyjne? (24)
- 1.2. Pisanie użytecznych programów bez efektów ubocznych (26)
- 1.3. W jaki sposób transparentność referencyjna czyni program bezpieczniejszym? (28)
- 1.4. Zalety programowania funkcyjnego (28)
- 1.5. Wykorzystanie modelu z zastępowaniem do rozumowania na temat programu (30)
- 1.6. Zastosowanie zasad funkcyjnych na prostym przykładzie (31)
- 1.7. Osiąganie limitów abstrakcji (36)
- 1.8. Podsumowanie (37)
Rozdział 2. Użycie funkcji w języku Java (39)
- 2.1. Czym jest funkcja? (40)
- 2.1.1. Funkcje w świecie rzeczywistym (40)
- 2.2. Funkcje w Javie (45)
- 2.2.1. Metody funkcyjne (45)
- 2.2.2. Interfejsy funkcyjne Javy i klasy anonimowe (50)
- 2.2.3. Złożenie funkcji (52)
- 2.2.4. Funkcje polimorficzne (52)
- 2.2.5. Upraszczanie kodu za pomocą funkcji anonimowych (53)
- 2.3. Zaawansowane funkcjonalności funkcji (55)
- 2.3.1. Co z funkcjami dotyczącymi kilku argumentów? (56)
- 2.3.2. Zastosowanie funkcji z częściowym rozwinięciem (57)
- 2.3.3. Funkcje wyższego rzędu (57)
- 2.3.4. Polimorficzne funkcje wyższego rzędu (58)
- 2.3.5. Użycie funkcji anonimowych (61)
- 2.3.6. Funkcje lokalne (63)
- 2.3.7. Domknięcia (64)
- 2.3.8. Częściowe zastosowanie funkcji i automatyczne rozwijanie (66)
- 2.3.9. Zamiana argumentów częściowo zastosowanych funkcji (70)
- 2.3.10. Funkcje rekurencyjne (71)
- 2.3.11. Funkcja tożsamościowa (73)
- 2.4. Interfejsy funkcyjne Javy 8 (74)
- 2.5. Debugging funkcji anonimowych (75)
- 2.6. Podsumowanie (78)
Rozdział 3. Uczynić Javę bardziej funkcyjną (79)
- 3.1. Zamiana standardowych struktur sterujących na ich funkcyjne odpowiedniki (80)
- 3.2. Abstrakcja struktur sterujących (81)
- 3.2.1. Czyszczenie kodu (85)
- 3.2.2. Alternatywa dla if ... else (88)
- 3.3. Abstrakcja iteracji (92)
- 3.3.1. Abstrakcja operacji na liście dzięki odwzorowaniu (94)
- 3.3.2. Tworzenie list (95)
- 3.3.3. Wykorzystanie operacji dotyczących głowy i ogona (96)
- 3.3.4. Funkcyjne dodawanie do listy (97)
- 3.3.5. Redukcja i zwijanie list (97)
- 3.3.6. Kompozycja odwzorowań i mapowanie kompozycji (103)
- 3.3.7. Stosowanie efektów dla list (104)
- 3.3.8. Funkcyjne podejście do danych wyjściowych (105)
- 3.3.9. Budowanie list referencji odwrotnych (106)
- 3.4. Zastosowanie właściwych typów (109)
- 3.4.1. Problemy ze standardowymi typami (109)
- 3.4.2. Definiowanie typów wartości (112)
- 3.4.3. Przyszłość typów wartości w Javie (115)
- 3.5. Podsumowanie (115)
Rozdział 4. Rekurencja, rekurencja odwrotna i memoizacja (117)
- 4.1. Różnice między rekurencją i rekurencją odwrotną (118)
- 4.1.1. Przykład z dodawaniem dla obu rodzajów rekurencji (118)
- 4.1.2. Implementacja rekurencji w Javie (119)
- 4.1.3. Wykorzystanie eliminacji wywołania ogonowego (119)
- 4.1.4. Użycie funkcji i metod z rekurencją ogonową (120)
- 4.1.5. Abstrakcja rekurencji (120)
- 4.1.6. Utworzenie wersji zapewniającej prostą podmianę metody rekurencyjnej bazującej na stosie (124)
- 4.2. Stosowanie funkcji rekurencyjnych (126)
- 4.2.1. Korzystanie z lokalnie zdefiniowanych funkcji (127)
- 4.2.2. Zapewnienie funkcji działających jako rekurencje ogonowe (128)
- 4.2.3. Funkcje podwójnie rekurencyjne - ciąg Fibonacciego (128)
- 4.2.4. Zamiana metod dla list na wersje rekurencyjne i bezpieczne dla stosu (131)
- 4.3. Kompozycja ogromnej liczby funkcji (134)
- 4.4. Korzystanie z memoizacji (137)
- 4.4.1. Memoizacja w programowaniu imperatywnym (137)
- 4.4.2. Memoizacja w funkcjach rekurencyjnych (138)
- 4.4.3. Memoizacja automatyczna (140)
- 4.5. Podsumowanie (146)
Rozdział 5. Obsługa danych przy użyciu list (147)
- 5.1. Jak klasyfikować kolekcje danych? (147)
- 5.1.1. Różne rodzaje list (148)
- 5.1.2. Względna oczekiwana wydajność listy (149)
- 5.1.3. Wymiana czasu na zajętość pamięci lub czasu kontra złożoność (150)
- 5.1.4. Modyfikacja na miejscu (151)
- 5.1.5. Trwałe struktury danych (152)
- 5.2. Implementacja niezmiennej, trwałej listy jednokierunkowej (153)
- 5.3. Współdzielenie danych w operacjach na liście (156)
- 5.3.1. Dodatkowe operacje na liście (158)
- 5.4. Wykorzystanie rekurencji do zwijania list za pomocą funkcji wyższego rzędu (163)
- 5.4.1. Bazująca na stercie, rekurencyjna wersja foldRight (169)
- 5.4.2. Odwzorowanie i filtrowanie list (171)
- 5.5. Podsumowanie (173)
Rozdział 6. Obsługa danych opcjonalnych (175)
- 6.1. Problemy ze wskaźnikiem null (176)
- 6.2. Alternatywy dla referencji null (177)
- 6.3. Typ danych Option (180)
- 6.3.1. Pobranie wartości z Option (182)
- 6.3.2. Stosowanie funkcji dla wartości opcjonalnych (184)
- 6.3.3. Kompozycja obiektów Option (185)
- 6.3.4. Sposoby użycia Option (187)
- 6.3.5. Inne sposoby łączenia opcji (191)
- 6.3.6. Kompozycja List z Option (193)
- 6.4. Różne narzędzia dodatkowe dla Option (195)
- 6.4.1. Testowanie, czy to Some, czy None (195)
- 6.4.2. Implementacja metod equals i hashcode (195)
- 6.5. Jak i gdzie używać Option? (196)
- 6.6. Podsumowanie (199)
Rozdział 7. Obsługa błędów i wyjątków (201)
- 7.1. Problemy do rozwiązania (201)
- 7.2. Typ Either (203)
- 7.2.1. Kompozycja klasy Either (204)
- 7.3. Typ Result (206)
- 7.3.1. Dodawanie metod do klasy Result (207)
- 7.4. Wzorce Result (209)
- 7.5. Zaawansowana obsługa Result (216)
- 7.5.1. Stosowanie predykatów (216)
- 7.5.2. Mapowanie porażek (217)
- 7.5.3. Dodanie metod fabrycznych (220)
- 7.5.4. Stosowanie efektów (221)
- 7.5.5. Zaawansowana kompozycja wyników (224)
- 7.6. Podsumowanie (227)
Rozdział 8. Zaawansowana obsługa list (229)
- 8.1. Problem z length (230)
- 8.1.1. Problem wydajności (230)
- 8.1.2. Zalety memoizacji (231)
- 8.1.3. Wady memoizacji (231)
- 8.1.4. Faktyczna wydajność (233)
- 8.2. Kompozycja List i Result (233)
- 8.2.1. Metody List zwracające Result (233)
- 8.2.2. Konwersja z List<Result> na Result<List> (235)
- 8.3. Abstrakcja typowych operacji na listach (238)
- 8.3.1. Zszywanie i rozszywanie list (238)
- 8.3.2. Dostęp do elementów na podstawie ich indeksów (241)
- 8.3.3. Dzielenie list (243)
- 8.3.4. Poszukiwanie podlist (247)
- 8.3.5. Różnorakie funkcje dotyczące obsługi list (248)
- 8.4. Automatyczne przetwarzanie równoległe list (251)
- 8.4.1. Nie wszystkie obliczenia można zrównoleglić (251)
- 8.4.2. Podział listy na podlisty (252)
- 8.4.3. Zrównoleglone przetwarzanie podlist (253)
- 8.5. Podsumowanie (255)
Rozdział 9. Wykorzystywanie leniwości obliczeń (257)
- 9.1. Zrozumieć rygor i lenistwo (258)
- 9.1.1. Java jest językiem rygorystycznym (258)
- 9.1.2. Problem z rygorem (259)
- 9.2. Implementacja wersji leniwej (261)
- 9.3. Rzeczy, których nie wykonamy bez lenistwa (262)
- 9.4. Dlaczego nie użyjemy klasy Stream z Javy 8? (263)
- 9.5. Tworzenie struktury danych dla leniwej listy (263)
- 9.5.1. Memoizacja wyliczonych wartości (265)
- 9.5.2. Modyfikacja strumienia (268)
- 9.6. Prawdziwa esencja lenistwa (271)
- 9.6.1. Zwijanie strumieni (273)
- 9.7. Obsługa strumieni nieskończonych (278)
- 9.8. Unikanie referencji null i modyfikowalnych pól (280)
- 9.9. Podsumowanie (282)
Rozdział 10. Obsługa danych za pomocą drzew (285)
- 10.1. Drzewo binarne (286)
- 10.1.1. Drzewa zrównoważone i niezbalansowane (287)
- 10.1.2. Rozmiar, wysokość i głębia (287)
- 10.1.3. Drzewa liściaste (288)
- 10.1.4. Uporządkowane drzewa binarne lub też drzewa binarne wyszukiwania (288)
- 10.1.5. Kolejność wstawiania (289)
- 10.1.6. Kolejność przejścia przez drzewo (290)
- 10.2. Implementacja drzewa binarnego (292)
- 10.3. Usuwanie elementów z drzew (298)
- 10.4. Łączenie dowolnych drzew (300)
- 10.5. Zwijanie drzewa (304)
- 10.5.1. Zwijanie za pomocą dwóch funkcji (305)
- 10.5.2. Zwijanie za pomocą jednej funkcji (307)
- 10.5.3. Którą implementację zwinięcia wybrać? (308)
- 10.6. Odwzorowanie drzew (310)
- 10.7. Równoważenie drzew (311)
- 10.7.1. Obracanie drzew (311)
- 10.7.2. Równoważenie drzew za pomocą algorytmu Day-Stout-Warren (314)
- 10.7.3. Automatycznie równoważące się drzewa (315)
- 10.7.4. Rozwiązywanie właściwego problemu (316)
- 10.8. Podsumowanie (317)
Rozdział 11. Rozwiązywanie rzeczywistych problemów przy użyciu zaawansowanych drzew (319)
- 11.1. Lepsza wydajność i bezpieczeństwo stosu dzięki samobalansującym się drzewom (320)
- 11.1.1. Prosta struktura drzewa (320)
- 11.1.2. Wstawianie elementu do drzewa czerwono-czarnego (325)
- 11.2. Przykład użycia drzew czerwono-czarnych - mapowanie (330)
- 11.2.1. Implementacja klasy Map (330)
- 11.2.2. Rozbudowania klasy Map (333)
- 11.2.3. Użycie klasy Map dla kluczy bez możliwości porównywania (334)
- 11.3. Implementacja funkcyjnej kolejki priorytetowej (336)
- 11.3.1. Protokół dostępowy dla kolejki priorytetowej (336)
- 11.3.2. Sposoby użycia kolejek priorytetowych (337)
- 11.3.3. Wymagania implementacyjne (337)
- 11.3.4. Struktura danych nazywana kopcem lewostronnym (338)
- 11.3.5. Implementacja kopca lewostronnego (338)
- 11.3.6. Implementacja interfejsu przypominającego kolejkę (343)
- 11.4. Kolejka priorytetowa dla elementów bez możliwości porównywania (344)
- 11.5. Podsumowanie (349)
Rozdział 12. Obsługa zmian stanu w sposób funkcyjny (351)
- 12.1. Funkcjonalny generator liczb losowych (352)
- 12.1.1. Interfejs generatora liczb losowych (353)
- 12.1.2. Implementacja generatora liczb losowych (354)
- 12.2. Ogólne API do obsługi stanu (357)
- 12.2.1. Korzystanie z operacji na stanie (358)
- 12.2.2. Kompozycja operacji na stanie (359)
- 12.3. Ogólna obsługa stanu (363)
- 12.3.1. Wzorce stanu (364)
- 12.3.2. Tworzenie maszyny stanowej (365)
- 12.3.3. Kiedy korzystać ze stanu i maszyny stanowej (370)
- 12.4. Podsumowanie (371)
Rozdział 13. Funkcyjne wejście-wyjście (373)
- 13.1. Stosowanie efektów w kontekście (374)
- 13.1.1. Czym są efekty? (374)
- 13.1.2. Implementacja efektów (375)
- 13.1.3. Bardziej użyteczne efekty dla porażek (377)
- 13.2. Odczyt danych (380)
- 13.2.1. Odczyt danych z konsoli (380)
- 13.2.2. Odczyt danych z pliku (384)
- 13.2.3. Testowanie z zadanymi danymi wejściowymi (386)
- 13.3. Naprawdę funkcyjne wejście-wyjście (387)
- 13.3.1. W jaki sposób zapewnić pełną funkcyjność wejścia-wyjścia? (387)
- 13.3.2. Implementacja w pełni funkcyjnego wejścia-wyjścia (388)
- 13.3.3. Łączenie operacji wejścia-wyjścia (389)
- 13.3.4. Obsługa wejścia za pomocą IO (390)
- 13.3.5. Rozszerzanie typu IO (393)
- 13.3.6. Uczynienie typu IO bezpiecznym dla stosu (395)
- 13.4. Podsumowanie (400)
Rozdział 14. Współdzielenie zmiennego stanu przy użyciu aktorów (401)
- 14.1. Model aktora (402)
- 14.1.1. Asynchroniczne komunikaty (403)
- 14.1.2. Obsługa zrównoleglenia (403)
- 14.1.3. Obsługa zmiany stanu aktora (404)
- 14.2. Budowanie frameworka aktora (405)
- 14.2.1. Ograniczenia prezentowanego frameworka aktora (405)
- 14.2.2. Projektowanie interfejsów frameworka aktorów (405)
- 14.2.3. Implementacja AbstractActor (407)
- 14.3. Zmuszenie aktorów do działania (408)
- 14.3.1. Implementacja przykładu z ping-pongiem (409)
- 14.3.2. Bardziej poważny przykład - równoległe wykonywanie obliczeń (410)
- 14.3.3. Zmiana kolejności wyników (415)
- 14.3.4. Rozwiązanie problemu wydajności (418)
- 14.4. Podsumowanie (423)
Rozdział 15. Rozwiązywanie typowych problemów w sposób funkcyjny (425)
- 15.1. Wykorzystanie asercji do walidacji danych (426)
- 15.2. Odczyt właściwości z pliku (430)
- 15.2.1. Wczytywanie pliku właściwości (430)
- 15.2.2. Odczyt właściwości jako tekstu (431)
- 15.2.3. Tworzenie lepszych komunikatów o błędzie (432)
- 15.2.4. Odczyt właściwości jako listy (435)
- 15.2.5. Odczytywanie wyliczeń (436)
- 15.2.6. Odczyt właściwości dowolnych typów (437)
- 15.3. Konwersja programu imperatywnego - czytnik plików XML (440)
- 15.3.1. Zebranie potrzebnych funkcji (441)
- 15.3.2. Kompozycja funkcji i stosowanie efektu (442)
- 15.3.3. Implementacja funkcji (443)
- 15.3.4. Uczynienie programu nawet bardziej funkcyjnym (444)
- 15.3.5. Rozwiązanie problemu z typem argumentu (448)
- 15.3.6. Zmiana funkcji przetwarzającej element na parametr (449)
- 15.3.7. Obsługa błędów dla nazw elementów (450)
- 15.4. Podsumowanie (451)
Dodatek A. Wykorzystanie elementów funkcyjnych Javy 8 (453)
- A.1. Klasa Optional (454)
- A.2. Strumienie (455)
Dodatek B. Monady (461)
Dodatek C. Co dalej? (467)
- C.1. Wybór nowego języka (467)
- C.1.1. Haskell (467)
- C.1.2. Scala (468)
- C.1.3. Kotlin (468)
- C.1.4. Frege (469)
- C.1.5. A co z dynamicznie typowanymi językami funkcyjnymi? (469)
- C.2. Pozostanie z Javą (469)
- C.2.1. Functional Java (470)
- C.2.2. Javaslang (470)
- C.2.3. Cyclops (470)
- C.2.4. Inne biblioteki funkcyjne (471)
- C.3. Dodatkowe lektury (471)
Skorowidz (473)
Kategoria: | Programowanie |
Zabezpieczenie: |
Watermark
|
ISBN: | 978-83-283-3325-3 |
Rozmiar pliku: | 4,0 MB |