Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00450 007372 10470655 na godz. na dobę w sumie
Java. Programowanie funkcyjne - ebook/pdf
Java. Programowanie funkcyjne - ebook/pdf
Autor: Liczba stron: 480
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-3325-3 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> java - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).

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:

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).

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Functional Programming in Java: How to improve your Java programs using functional techniques Tłumaczenie: Rafał Jońca ISBN: 978-83-283-3324-6 Original edition copyright © 2017 by Manning Publications Co. All rights reserved. Polish edition copyright © 2017 by Helion SA All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/javapf.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/javapf Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:258)ci Przedmowa 13 Podzi(cid:218)kowania 17 O ksi(cid:200)(cid:285)ce 19 Rozdzia(cid:239) 1. Czym jest programowanie funkcyjne? 23 Zalety programowania funkcyjnego 28 Pisanie u(cid:285)ytecznych programów bez efektów ubocznych 26 1.1. Czym jest programowanie funkcyjne? 24 1.2. 1.3. W jaki sposób transparentno(cid:258)(cid:202) referencyjna czyni program bezpieczniejszym? 28 1.4. 1.5. Wykorzystanie modelu z zast(cid:218)powaniem do rozumowania na temat programu 30 1.6. 1.7. Osi(cid:200)ganie limitów abstrakcji 36 1.8. Zastosowanie zasad funkcyjnych na prostym przyk(cid:239)adzie 31 Podsumowanie 37 Rozdzia(cid:239) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java 39 2.1. Czym jest funkcja? 40 2.1.1. Funkcje w (cid:258)wiecie rzeczywistym 40 2.2. Funkcje w Javie 45 2.3. Interfejsy funkcyjne Javy i klasy anonimowe 50 Z(cid:239)o(cid:285)enie funkcji 52 Funkcje polimorficzne 52 2.2.1. Metody funkcyjne 45 2.2.2. 2.2.3. 2.2.4. 2.2.5. Upraszczanie kodu za pomoc(cid:200) funkcji anonimowych 53 Zaawansowane funkcjonalno(cid:258)ci funkcji 55 2.3.1. Co z funkcjami dotycz(cid:200)cymi kilku argumentów? 56 Zastosowanie funkcji z cz(cid:218)(cid:258)ciowym rozwini(cid:218)ciem 57 2.3.2. Funkcje wy(cid:285)szego rz(cid:218)du 57 2.3.3. 2.3.4. Polimorficzne funkcje wy(cid:285)szego rz(cid:218)du 58 2.3.5. U(cid:285)ycie funkcji anonimowych 61 2.3.6. 2.3.7. Domkni(cid:218)cia 64 2.3.8. Cz(cid:218)(cid:258)ciowe zastosowanie funkcji i automatyczne rozwijanie 66 2.3.9. 2.3.10. Funkcje rekurencyjne 71 2.3.11. Funkcja to(cid:285)samo(cid:258)ciowa 73 Interfejsy funkcyjne Javy 8 74 Zamiana argumentów cz(cid:218)(cid:258)ciowo zastosowanych funkcji 70 Funkcje lokalne 63 2.4. 2.5. Debugging funkcji anonimowych 75 2.6. Podsumowanie 78 Poleć książkęKup książkę 6 Spis tre(cid:258)ci Rozdzia(cid:239) 3. Uczyni(cid:202) Jav(cid:218) bardziej funkcyjn(cid:200) 79 Zamiana standardowych struktur steruj(cid:200)cych na ich funkcyjne odpowiedniki 80 3.1. 3.2. Abstrakcja struktur steruj(cid:200)cych 81 3.2.1. Czyszczenie kodu 85 3.2.2. Alternatywa dla if ... else 88 3.3. Abstrakcja iteracji 92 Tworzenie list 95 Funkcyjne dodawanie do listy 97 3.3.1. Abstrakcja operacji na li(cid:258)cie dzi(cid:218)ki odwzorowaniu 94 3.3.2. 3.3.3. Wykorzystanie operacji dotycz(cid:200)cych g(cid:239)owy i ogona 96 3.3.4. 3.3.5. Redukcja i zwijanie list 97 3.3.6. Kompozycja odwzorowa(cid:241) i mapowanie kompozycji 103 3.3.7. 3.3.8. 3.3.9. Budowanie list referencji odwrotnych 106 Zastosowanie w(cid:239)a(cid:258)ciwych typów 109 3.4.1. 3.4.2. Definiowanie typów warto(cid:258)ci 112 3.4.3. Podsumowanie 115 Stosowanie efektów dla list 104 Funkcyjne podej(cid:258)cie do danych wyj(cid:258)ciowych 105 Problemy ze standardowymi typami 109 Przysz(cid:239)o(cid:258)(cid:202) typów warto(cid:258)ci w Javie 115 3.4. 3.5. Rozdzia(cid:239) 4. Rekurencja, rekurencja odwrotna i memoizacja 117 4.1. Ró(cid:285)nice mi(cid:218)dzy rekurencj(cid:200) i rekurencj(cid:200) odwrotn(cid:200) 118 Przyk(cid:239)ad z dodawaniem dla obu rodzajów rekurencji 118 Implementacja rekurencji w Javie 119 4.1.1. 4.1.2. 4.1.3. Wykorzystanie eliminacji wywo(cid:239)ania ogonowego 119 4.1.4. U(cid:285)ycie funkcji i metod z rekurencj(cid:200) ogonow(cid:200) 120 4.1.5. Abstrakcja rekurencji 120 4.1.6. Utworzenie wersji zapewniaj(cid:200)cej prost(cid:200) podmian(cid:218) metody rekurencyjnej bazuj(cid:200)cej na stosie 124 4.2. Stosowanie funkcji rekurencyjnych 126 4.2.1. Korzystanie z lokalnie zdefiniowanych funkcji 127 4.2.2. 4.2.3. 4.2.4. Zapewnienie funkcji dzia(cid:239)aj(cid:200)cych jako rekurencje ogonowe 128 Funkcje podwójnie rekurencyjne — ci(cid:200)g Fibonacciego 128 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 Podsumowanie 146 4.5. Rozdzia(cid:239) 5. Obs(cid:239)uga danych przy u(cid:285)yciu list 147 5.1. Jak klasyfikowa(cid:202) kolekcje danych? 147 5.1.1. Ró(cid:285)ne rodzaje list 148 5.1.2. Wzgl(cid:218)dna oczekiwana wydajno(cid:258)(cid:202) listy 149 5.1.3. Wymiana czasu na zaj(cid:218)to(cid:258)(cid:202) pami(cid:218)ci lub czasu kontra z(cid:239)o(cid:285)ono(cid:258)(cid:202) 150 Poleć książkęKup książkę Spis tre(cid:258)ci 7 5.1.4. Modyfikacja na miejscu 151 5.1.5. Trwa(cid:239)e struktury danych 152 Implementacja niezmiennej, trwa(cid:239)ej listy jednokierunkowej 153 5.2. 5.3. Wspó(cid:239)dzielenie danych w operacjach na li(cid:258)cie 156 5.3.1. Dodatkowe operacje na li(cid:258)cie 158 5.4. Wykorzystanie rekurencji do zwijania list za pomoc(cid:200) funkcji wy(cid:285)szego rz(cid:218)du 163 5.4.1. Bazuj(cid:200)ca na stercie, rekurencyjna wersja foldRight 169 5.4.2. Odwzorowanie i filtrowanie list 171 Podsumowanie 173 5.5. Rozdzia(cid:239) 6. Obs(cid:239)uga danych opcjonalnych 175 6.1. Problemy ze wska(cid:283)nikiem null 176 6.2. Alternatywy dla referencji null 177 6.3. Pobranie warto(cid:258)ci z Option 182 Stosowanie funkcji dla warto(cid:258)ci opcjonalnych 184 Typ danych Option 180 6.3.1. 6.3.2. 6.3.3. Kompozycja obiektów Option 185 Sposoby u(cid:285)ycia Option 187 6.3.4. 6.3.5. Inne sposoby (cid:239)(cid:200)czenia opcji 191 6.3.6. Kompozycja List z Option 193 6.4. Ró(cid:285)ne narz(cid:218)dzia dodatkowe dla Option 195 Testowanie, czy to Some, czy None 195 Implementacja metod equals i hashcode 195 6.4.1. 6.4.2. Jak i gdzie u(cid:285)ywa(cid:202) Option? 196 Podsumowanie 199 6.5. 6.6. Rozdzia(cid:239) 7. Obs(cid:239)uga b(cid:239)(cid:218)dów i wyj(cid:200)tków 201 Problemy do rozwi(cid:200)zania 201 7.1. 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(cid:239)uga Result 216 7.5.1. Stosowanie predykatów 216 7.5.2. Mapowanie pora(cid:285)ek 217 7.5.3. Dodanie metod fabrycznych 220 7.5.4. 7.5.5. Podsumowanie 227 Stosowanie efektów 221 Zaawansowana kompozycja wyników 224 7.6. Rozdzia(cid:239) 8. Zaawansowana obs(cid:239)uga list 229 8.1. Problem z length 230 Problem wydajno(cid:258)ci 230 8.1.1. 8.1.2. Zalety memoizacji 231 8.1.3. Wady memoizacji 231 8.1.4. Faktyczna wydajno(cid:258)(cid:202) 233 Poleć książkęKup książkę 8 Spis tre(cid:258)ci 8.2. Kompozycja List i Result 233 8.2.1. Metody List zwracaj(cid:200)ce Result 233 8.2.2. Konwersja z List Result na Result List 235 8.3. Abstrakcja typowych operacji na listach 238 Zszywanie i rozszywanie list 238 8.3.1. 8.3.2. Dost(cid:218)p do elementów na podstawie ich indeksów 241 8.3.3. Dzielenie list 243 8.3.4. 8.3.5. Ró(cid:285)norakie funkcje dotycz(cid:200)ce obs(cid:239)ugi list 248 Poszukiwanie podlist 247 8.4. Automatyczne przetwarzanie równoleg(cid:239)e list 251 8.4.1. Nie wszystkie obliczenia mo(cid:285)na zrównolegli(cid:202) 251 8.4.2. 8.4.3. Podsumowanie 255 Podzia(cid:239) listy na podlisty 252 Zrównoleglone przetwarzanie podlist 253 8.5. Rozdzia(cid:239) 9. Wykorzystywanie leniwo(cid:258)ci oblicze(cid:241) 257 9.1. Zrozumie(cid:202) rygor i lenistwo 258 9.1.1. 9.1.2. Implementacja wersji leniwej 261 Java jest j(cid:218)zykiem rygorystycznym 258 Problem z rygorem 259 9.2. 9.3. Rzeczy, których nie wykonamy bez lenistwa 262 9.4. Dlaczego nie u(cid:285)yjemy klasy Stream z Javy 8? 263 Tworzenie struktury danych dla leniwej listy 263 9.5. 9.5.1. Memoizacja wyliczonych warto(cid:258)ci 265 9.5.2. Modyfikacja strumienia 268 Prawdziwa esencja lenistwa 271 9.6.1. Zwijanie strumieni 273 9.6. 9.7. Obs(cid:239)uga strumieni niesko(cid:241)czonych 278 9.8. Unikanie referencji null i modyfikowalnych pól 280 9.9. Podsumowanie 282 Rozdzia(cid:239) 10. Obs(cid:239)uga danych za pomoc(cid:200) drzew 285 10.1. Drzewo binarne 286 10.1.1. Drzewa zrównowa(cid:285)one i niezbalansowane 287 10.1.2. Rozmiar, wysoko(cid:258)(cid:202) i g(cid:239)(cid:218)bia 287 10.1.3. Drzewa li(cid:258)ciaste 288 10.1.4. Uporz(cid:200)dkowane drzewa binarne lub te(cid:285) drzewa binarne wyszukiwania 288 10.1.5. Kolejno(cid:258)(cid:202) wstawiania 289 10.1.6. Kolejno(cid:258)(cid:202) przej(cid:258)cia przez drzewo 290 10.2. Implementacja drzewa binarnego 292 10.3. Usuwanie elementów z drzew 298 10.4. (cid:146)(cid:200)czenie dowolnych drzew 300 10.5. Zwijanie drzewa 304 10.5.1. Zwijanie za pomoc(cid:200) dwóch funkcji 305 10.5.2. Zwijanie za pomoc(cid:200) jednej funkcji 307 10.5.3. Któr(cid:200) implementacj(cid:218) zwini(cid:218)cia wybra(cid:202)? 308 Poleć książkęKup książkę Spis tre(cid:258)ci 9 10.6. Odwzorowanie drzew 310 10.7. Równowa(cid:285)enie drzew 311 10.7.1. Obracanie drzew 311 10.7.2. Równowa(cid:285)enie drzew za pomoc(cid:200) algorytmu Day-Stout-Warren 314 10.7.3. Automatycznie równowa(cid:285)(cid:200)ce si(cid:218) drzewa 315 10.7.4. Rozwi(cid:200)zywanie w(cid:239)a(cid:258)ciwego problemu 316 10.8. Podsumowanie 317 Rozdzia(cid:239) 11. Rozwi(cid:200)zywanie rzeczywistych problemów przy u(cid:285)yciu zaawansowanych drzew 319 11.1. Lepsza wydajno(cid:258)(cid:202) i bezpiecze(cid:241)stwo stosu dzi(cid:218)ki samobalansuj(cid:200)cym si(cid:218) drzewom 320 11.1.1. Prosta struktura drzewa 320 11.1.2. Wstawianie elementu do drzewa czerwono-czarnego 325 11.2. Przyk(cid:239)ad u(cid:285)ycia drzew czerwono-czarnych — mapowanie 330 11.2.1. Implementacja klasy Map 330 11.2.2. Rozbudowania klasy Map 333 11.2.3. U(cid:285)ycie klasy Map dla kluczy bez mo(cid:285)liwo(cid:258)ci porównywania 334 11.3. Implementacja funkcyjnej kolejki priorytetowej 336 11.3.1. Protokó(cid:239) dost(cid:218)powy dla kolejki priorytetowej 336 11.3.2. Sposoby u(cid:285)ycia kolejek priorytetowych 337 11.3.3. Wymagania implementacyjne 337 11.3.4. Struktura danych nazywana kopcem lewostronnym 338 11.3.5. 11.3.6. Implementacja kopca lewostronnego 338 Implementacja interfejsu przypominaj(cid:200)cego kolejk(cid:218) 343 11.4. Kolejka priorytetowa dla elementów bez mo(cid:285)liwo(cid:258)ci porównywania 344 11.5. Podsumowanie 349 Rozdzia(cid:239) 12. Obs(cid:239)uga zmian stanu w sposób funkcyjny 351 12.1. Funkcjonalny generator liczb losowych 352 12.1.1. 12.1.2. Interfejs generatora liczb losowych 353 Implementacja generatora liczb losowych 354 12.2. Ogólne API do obs(cid:239)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(cid:239)uga stanu 363 12.3.1. Wzorce stanu 364 12.3.2. Tworzenie maszyny stanowej 365 12.3.3. Kiedy korzysta(cid:202) ze stanu i maszyny stanowej 370 12.4. Podsumowanie 371 Rozdzia(cid:239) 13. Funkcyjne wej(cid:258)cie-wyj(cid:258)cie 373 13.1. Stosowanie efektów w kontek(cid:258)cie 374 13.1.1. Czym s(cid:200) efekty? 374 13.1.2. 13.1.3. Bardziej u(cid:285)yteczne efekty dla pora(cid:285)ek 377 Implementacja efektów 375 Poleć książkęKup książkę 10 Spis tre(cid:258)ci 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(cid:258)ciowymi 386 13.3. Naprawd(cid:218) funkcyjne wej(cid:258)cie-wyj(cid:258)cie 387 Implementacja w pe(cid:239)ni funkcyjnego wej(cid:258)cia-wyj(cid:258)cia 388 13.3.1. W jaki sposób zapewni(cid:202) pe(cid:239)n(cid:200) funkcyjno(cid:258)(cid:202) wej(cid:258)cia-wyj(cid:258)cia? 387 13.3.2. 13.3.3. (cid:146)(cid:200)czenie operacji wej(cid:258)cia-wyj(cid:258)cia 389 13.3.4. Obs(cid:239)uga wej(cid:258)cia za pomoc(cid:200) IO 390 13.3.5. Rozszerzanie typu IO 393 13.3.6. Uczynienie typu IO bezpiecznym dla stosu 395 13.4. Podsumowanie 400 Rozdzia(cid:239) 14. Wspó(cid:239)dzielenie zmiennego stanu przy u(cid:285)yciu aktorów 401 14.1. Model aktora 402 14.1.1. Asynchroniczne komunikaty 403 14.1.2. Obs(cid:239)uga zrównoleglenia 403 14.1.3. Obs(cid:239)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(cid:239)ania 408 Implementacja przyk(cid:239)adu z ping-pongiem 409 14.3.1. 14.3.2. Bardziej powa(cid:285)ny przyk(cid:239)ad — równoleg(cid:239)e wykonywanie oblicze(cid:241) 410 14.3.3. Zmiana kolejno(cid:258)ci wyników 415 14.3.4. Rozwi(cid:200)zanie problemu wydajno(cid:258)ci 418 14.4. Podsumowanie 423 Rozdzia(cid:239) 15. Rozwi(cid:200)zywanie typowych problemów w sposób funkcyjny 425 15.1. Wykorzystanie asercji do walidacji danych 426 15.2. Odczyt w(cid:239)a(cid:258)ciwo(cid:258)ci z pliku 430 15.2.1. Wczytywanie pliku w(cid:239)a(cid:258)ciwo(cid:258)ci 430 15.2.2. Odczyt w(cid:239)a(cid:258)ciwo(cid:258)ci jako tekstu 431 15.2.3. Tworzenie lepszych komunikatów o b(cid:239)(cid:218)dzie 432 15.2.4. Odczyt w(cid:239)a(cid:258)ciwo(cid:258)ci jako listy 435 15.2.5. Odczytywanie wylicze(cid:241) 436 15.2.6. Odczyt w(cid:239)a(cid:258)ciwo(cid:258)ci dowolnych typów 437 15.3. Konwersja programu imperatywnego — czytnik plików XML 440 Implementacja funkcji 443 15.3.1. Zebranie potrzebnych funkcji 441 15.3.2. Kompozycja funkcji i stosowanie efektu 442 15.3.3. 15.3.4. Uczynienie programu nawet bardziej funkcyjnym 444 15.3.5. Rozwi(cid:200)zanie problemu z typem argumentu 448 15.3.6. Zmiana funkcji przetwarzaj(cid:200)cej element na parametr 449 15.3.7. Obs(cid:239)uga b(cid:239)(cid:218)dów dla nazw elementów 450 15.4. Podsumowanie 451 Poleć książkęKup książkę Dodatek A. Wykorzystanie elementów funkcyjnych Javy 8 453 Spis tre(cid:258)ci 11 A.1. Klasa Optional 454 A.2. Strumienie 455 Dodatek B. Monady 461 Dodatek C. Co dalej? 467 C.1. Wybór nowego j(cid:218)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(cid:218)zykami funkcyjnymi? 469 C.2. Pozostanie z Jav(cid:200) 469 Javaslang 470 C.2.1. Functional Java 470 C.2.2. C.2.3. Cyclops 470 C.2.4. Inne biblioteki funkcyjne 471 C.3. Dodatkowe lektury 471 Skorowidz 473 Poleć książkęKup książkę 12 Spis tre(cid:258)ci Poleć książkęKup książkę U(cid:285)ycie funkcji w j(cid:218)zyku Java W tym rozdziale: (cid:81) dzia(cid:225)anie funkcji w rzeczywistym (cid:286)wiecie; (cid:81) sposób reprezentacji funkcji w Javie; (cid:81) u(cid:298)ycie lambd; (cid:81) praca z funkcjami wy(cid:298)szego rz(cid:266)du; (cid:81) rozwijanie funkcji (ang. currying); (cid:81) programowanie z u(cid:298)yciem interfejsów funkcyjnych. Aby zrozumie(cid:202), jak dzia(cid:239)a programowanie funkcyjne, mogliby(cid:258)my u(cid:285)y(cid:202) komponentów funkcyjnych zapewnianych przez biblioteczk(cid:218) stworzon(cid:200) do tego celu (powsta(cid:239)o nawet kilka nakierowanych na Jav(cid:218) 8). Zamiast tego postaramy si(cid:218) wszystko skonstruowa(cid:202) samo- dzielnie i nie korzysta(cid:202) z gotowych komponentów. Po opanowaniu wszystkich elementów b(cid:218)dziesz móg(cid:239) sam wybra(cid:202) mi(cid:218)dzy w(cid:239)asnymi funkcjami a tymi zapewnianymi przez Jav(cid:218) 8 czy te(cid:285) przez zewn(cid:218)trzne biblioteki. W tym rozdziale wykonamy interfejs Function, bardzo podobny do interfejsu Function z Javy 8. Rozwi(cid:200)zanie b(cid:218)dzie uproszczone w kwestii obs(cid:239)ugi parametrów typów (unikamy elementów wieloznacznych), aby u(cid:239)atwi(cid:202) zrozu- mienie kodu, ale z drugiej strony b(cid:218)dzie zawiera(cid:239)o kilka funkcjonalno(cid:258)ci, których bra- kuje w wersji dost(cid:218)pnej w Javie 8. Poza tymi ró(cid:285)nicami oba rozwi(cid:200)zania b(cid:218)d(cid:200) w zasa- dzie wymienne. Mog(cid:200) pojawi(cid:202) si(cid:218) trudno(cid:258)ci ze zrozumieniem niektórych fragmentów kodu przedsta- wianych w tym rozdziale. To normalne, poniewa(cid:285) bardzo trudno wprowadzi(cid:202) funkcje bez korzystania z innych konstrukcji funkcyjnych, takich jak List, Option itp. B(cid:200)d(cid:283) cierpliwy. Wszystkie nieopisane tu elementy zostan(cid:200) wyja(cid:258)nione w nast(cid:218)pnych rozdzia(cid:239)ach. Poleć książkęKup książkę 40 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java Wyja(cid:258)ni(cid:218) bardzo szczegó(cid:239)owo, czym jest funkcja — zarówno w (cid:258)wiecie rzeczywistym, jak i w j(cid:218)zyku programowania. Funkcje nie s(cid:200) tylko czym(cid:258), co istnieje w matematyce czy j(cid:218)zyku programowania. Funkcje stanowi(cid:200) cz(cid:218)(cid:258)(cid:202) codziennego (cid:285)ycia. Ca(cid:239)y czas mode- lujemy (cid:258)wiat, w którym (cid:285)yjemy; nie dotyczy to tylko programowania. Tworzymy pewne interpretacje (cid:258)wiata wokó(cid:239) nas. Reprezentacje (cid:258)wiata bardzo cz(cid:218)sto bazuj(cid:200) na obiektach, które modyfikuj(cid:200) swój stan wraz ze zmian(cid:200) czasu. Ten sposób interpretacji le(cid:285)y w natu- rze cz(cid:239)owieka. Przej(cid:258)cie od stanu A do stanu B wymaga czasu i ma zwi(cid:200)zany z tym koszt w postaci czasu, wysi(cid:239)ku i pieni(cid:218)dzy. We(cid:283)my jako przyk(cid:239)ad dodawanie. Wi(cid:218)kszo(cid:258)(cid:202) z nas traktuje dodawanie jako obliczenie wymagaj(cid:200)ce czasu (a w pewnych sytuacjach nawet wysi(cid:239)ku intelektualnego!). Ma pewien stan pocz(cid:200)tkowy, przej(cid:258)cie (obliczenia) i stan ko(cid:241)cowy (wynik dodawania). Aby doda(cid:202) do siebie 345 765 i 34 524, z pewno(cid:258)ci(cid:200) musimy wykona(cid:202) pewne obli- czenia. Niektórym zajmie to tylko chwilk(cid:218), innym nieco wi(cid:218)cej czasu. Niektórym nigdy si(cid:218) to nie uda lub otrzymaj(cid:200) b(cid:239)(cid:218)dny wynik. Niektórzy do oblicze(cid:241) b(cid:218)d(cid:200) potrzebowali kartki i o(cid:239)ówka, a innym wystarczy g(cid:239)owa. Wszyscy z pewno(cid:258)ci(cid:200) w trakcie oblicze(cid:241) b(cid:218)d(cid:200) zmienia(cid:202) stan, niezale(cid:285)nie do tego, czy w g(cid:239)owie, czy na papierze. Z drugiej strony, aby doda(cid:202) 2 do 3, nie potrzebujemy tego wszystkiego. Wi(cid:218)kszo(cid:258)(cid:202) z nas zna odpowied(cid:283) na pami(cid:218)(cid:202), wi(cid:218)c mo(cid:285)e jej udzieli(cid:202) bez przeprowadzania jakichkolwiek oblicze(cid:241). Ten przyk(cid:239)ad pokazuje, (cid:285)e obliczenia nie s(cid:200) elementem niezb(cid:218)dnym. Stanowi(cid:200) jedy- nie (cid:258)rodek do wskazania wyniku funkcji. Wynik istnia(cid:239), zanim dokonali(cid:258)my oblicze(cid:241) — po prostu jeszcze go nie znali(cid:258)my. Programowanie funkcyjne to programowanie z u(cid:285)yciem funkcji. Aby go u(cid:285)y(cid:202), musimy najpierw zrozumie(cid:202), czym jest funkcja — zarówno w (cid:258)wiecie rzeczywistym, jak i w wybra- nym j(cid:218)zyku programowania. 2.1. Czym jest funkcja? Funkcja znana jest jako pewien byt matematyczny, cho(cid:202) sama koncepcja dotyczy te(cid:285) (cid:285)ycia codziennego. Niestety, w (cid:285)yciu codziennym bardzo cz(cid:218)sto mylimy funkcj(cid:218) i efekty. Co gorsza, ten sam b(cid:239)(cid:200)d pope(cid:239)niamy równie(cid:285) w trakcie korzystania z j(cid:218)zyków programowania. 2.1.1. Funkcje w (cid:286)wiecie rzeczywistym W (cid:258)wiecie rzeczywistym funkcja to przede wszystkim koncepcja matematyczna. To zwi(cid:200)- zek mi(cid:218)dzy zbiorem (cid:283)ród(cid:239)owym, nazywanym dziedzin(cid:200) funkcji, a zbiorem docelowym, nazywanym przeciwdziedzin(cid:200) funkcji. Dziedzina i przeciwdziedzina nie musz(cid:200) si(cid:218) ró(cid:285)- ni(cid:202). Na przyk(cid:239)ad, funkcja mo(cid:285)e posiada(cid:202) ten sam zbiór liczb ca(cid:239)kowitych w dziedzinie i przeciwdziedzinie. CO CZYNI RELACJ(cid:265) MI(cid:265)DZY DWOMA ZBIORAMI FUNKCJ(cid:260)? Relacja, aby by(cid:239)(cid:200) funkcj(cid:200), musi spe(cid:239)ni(cid:202) jeden warunek — wszystkie elementy dziedziny musz(cid:200) mie(cid:202) jeden i tylko jeden odpowiadaj(cid:200)cy im element w przeciwdziedzinie, co przed- stawia rysunek 2.1. Poleć książkęKup książkę 2.1. Czym jest funkcja? 41 Rysunek 2.1. Wszystkie elementy dziedziny musz(cid:261) mie(cid:252) jeden i tylko jeden odpowiadaj(cid:261)cy im element w przeciwdziedzinie Ma to pewne interesuj(cid:200)ce implikacje: (cid:81) Nie mo(cid:285)e istnie(cid:202) element dziedziny, który nie posiada odpowiadaj(cid:200)cej mu warto- (cid:81) Nie mog(cid:200) istnie(cid:202) dwa elementy w przeciwdziedzinie odpowiadaj(cid:200)ce temu samemu (cid:81) Mog(cid:200) istnie(cid:202) w przeciwdziedzinie elementy, którym nie odpowiada (cid:285)aden element (cid:258)ci w przeciwdziedzinie. elementowi dziedziny. zbioru (cid:283)ród(cid:239)owego. ze zbioru (cid:283)ród(cid:239)owego. (cid:81) Mog(cid:200) istnie(cid:202) w przeciwdziedzinie elementy, które odpowiadaj(cid:200) kilku elementom (cid:81) Zbiór elementów przeciwdziedziny, które posiadaj(cid:200) odpowiadaj(cid:200)ce im elementy dziedziny, nazywa si(cid:218) obrazem funkcji. Rysunek 2.1 ilustruje funkcj(cid:218). Zdefiniujmy nast(cid:218)puj(cid:200)c(cid:200) funkcj(cid:218): f(x) = x + 1 w której x to liczba dodatnia. Funkcja reprezentuje zwi(cid:200)zek mi(cid:218)dzy ka(cid:285)d(cid:200) liczb(cid:200) dodatni(cid:200) i jej nast(cid:218)pczyni(cid:200). Mo(cid:285)emy funkcji nada(cid:202) nazw(cid:218). W szczególno(cid:258)ci mo(cid:285)emy nada(cid:202) nazw(cid:218), która pozwoli przypomnie(cid:202) sobie dzia(cid:239)anie funkcji: nast(cid:250)pca(x) = x + 1 Wydaje si(cid:218) to dobrym pomys(cid:239)em, ale nie nale(cid:285)y (cid:258)lepo ufa(cid:202) nazwie funkcji. Przecie(cid:285) kto(cid:258) móg(cid:239) nazwa(cid:202) funkcj(cid:218) w poni(cid:285)szy sposób: poprzednik(x) = x + 1 Nie jest to b(cid:239)(cid:200)d, bo nie istnieje (cid:285)aden oficjalny zwi(cid:200)zek mi(cid:218)dzy nazw(cid:200) funkcji a jej defi- nicj(cid:200). Oczywi(cid:258)cie, u(cid:285)ycie takiej nazwy nie jest dobrym pomys(cid:239)em. Poleć książkęKup książkę 42 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java Zauwa(cid:285), (cid:285)e mówimy tutaj o tym, czym funkcja jest (jej definicja), a nie o tym, co robi. Funkcja nic nie robi. Funkcja nast(cid:250)pca nie dodaje 1 do argumentu. Ty mo(cid:285)esz doda(cid:202) 1 do warto(cid:258)ci ca(cid:239)kowitej i obliczy(cid:202) nast(cid:218)pc(cid:218), ale Ty nie jeste(cid:258) funkcj(cid:200). Funkcja: nast(cid:250)pca(x) nie dodaje 1 do x. Jest jedynie równowa(cid:285)na x + 1, co oznacza, (cid:285)e za ka(cid:285)dym razem, gdy natkniesz si(cid:218) na wyra(cid:285)enie nast(cid:250)pca(x), mo(cid:285)esz je zamieni(cid:202) na (x + 1). Nawiasy stosuje si(cid:218) tylko w celu odizolowania wyra(cid:285)enia. Je(cid:258)li wyra(cid:285)enie stosuje si(cid:218) w odosobnieniu, s(cid:200) zb(cid:218)dne, cho(cid:202) w wielu momentach rozja(cid:258)niaj(cid:200) sytuacj(cid:218). FUNKCJE ODWROTNE Funkcja mo(cid:285)e posiada(cid:202) funkcj(cid:218) odwrotn(cid:200), ale nie musi. Je(cid:258)li f(x) to funkcja od A do B (A jest dziedzin(cid:200), a B przeciwdziedzin(cid:200)), funkcj(cid:218) odwrotn(cid:200) zapisuje si(cid:218) jako f-1(x) (teraz B jest dziedzin(cid:200), a A przeciwdziedzin(cid:200)). Je(cid:258)li typ funkcji wyrazimy jako A - B, to funkcja odwrotna (je(cid:258)li istnieje) ma typ B - A. Funkcja odwrotna jest funkcj(cid:200), je(cid:258)li spe(cid:239)ni te same warunki, jak ka(cid:285)da inna funkcja, czyli jedna i tylko jedna warto(cid:258)(cid:202) docelowa dla ka(cid:285)dej (cid:283)ród(cid:239)owej. Oznacza to, (cid:285)e funkcj(cid:200) odwrotn(cid:200) dla nast(cid:250)pca(x) b(cid:218)dzie relacja poprzednik(x) (oczywi(cid:258)cie nazwa jest dowolna). Nie jest to jednak funkcja w N (zbiór liczb ca(cid:239)kowitych dodatnich w(cid:239)(cid:200)cznie z 0), ponie- wa(cid:285) dla 0 nie ma w N poprzednika. Je(cid:258)li jednak funkcj(cid:218) nast(cid:250)pca(x) rozwa(cid:285)amy w zbiorze liczb ca(cid:239)kowitych ze znakiem (warto(cid:258)ci dodatnie i ujemne, oznaczane jako C), posiada ona funkcj(cid:218) odwrotn(cid:200) w postaci poprzednik(x). Niektóre proste funkcje nie posiadaj(cid:200) funkcji odwrotnych. Oto przyk(cid:239)ad: f(x) = (2 * x) Powy(cid:285)sza funkcja nie ma funkcji odwrotnej, je(cid:258)li jest definiowana jako przej(cid:258)cie z N do N. Posiada jednak funkcj(cid:218) odwrotn(cid:200), je(cid:258)li stanowi funkcj(cid:218) przej(cid:258)cia z N do zbioru liczb ca(cid:239)kowitych parzystych. FUNKCJE CZ(cid:265)(cid:285)CIOWE Relacja, która nie jest zdefiniowana dla wszystkich elementów dziedziny, ale która spe(cid:239)- nia pozosta(cid:239)e wymagania ((cid:285)aden element dziedziny nie ma wi(cid:218)cej ni(cid:285) jednej relacji z elementem z przeciwdziedziny), nazywana jest cz(cid:218)sto funkcj(cid:200) cz(cid:218)(cid:258)ciow(cid:200). Relacja poprzednik(x) jest funkcj(cid:200) cz(cid:218)(cid:258)ciow(cid:200) w zbiorze N (liczby dodatnie i 0), ale jest funkcj(cid:200) pe(cid:239)n(cid:200) w zbiorze N+ (liczby ca(cid:239)kowite dodatnie bez 0). Jej przeciwdziedzin(cid:200) jest N. Funkcje cz(cid:218)(cid:258)ciowe s(cid:200) bardzo wa(cid:285)ne w trakcie programowania, poniewa(cid:285) wiele b(cid:239)(cid:218)- dów wynika z faktu, i(cid:285) u(cid:285)yto funkcji cz(cid:218)(cid:258)ciowej w taki sposób, jakby by(cid:239)a funkcj(cid:200) pe(cid:239)n(cid:200). Przyk(cid:239)adowo, relacja f(x) = 1/x jest funkcj(cid:200) cz(cid:218)(cid:258)ciow(cid:200) z N do W (liczby wymierne), ponie- wa(cid:285) nie jest zdefiniowana dla 0. Jest funkcj(cid:200) pe(cid:239)n(cid:200) z N+ do W, jak i dla przej(cid:258)cia z N do W plus b(cid:239)(cid:200)d. Dodaj(cid:200)c element do przeciwdziedziny (b(cid:239)(cid:200)d), mo(cid:285)emy przekszta(cid:239)ci(cid:202) funkcj(cid:218) cz(cid:218)(cid:258)ciow(cid:200) w funkcj(cid:218) pe(cid:239)n(cid:200). Oznacza to jednak, (cid:285)e funkcja potrzebuje jakiego(cid:258) sposobu, aby zwróci(cid:202) b(cid:239)(cid:200)d. Czy widzisz ju(cid:285) analogi(cid:218) do programów komputerowych? Przekonasz si(cid:218), (cid:285)e zamiana funkcji cz(cid:218)(cid:258)ciowych na pe(cid:239)ne stanowi jeden z istotnych elementów pro- gramowania funkcyjnego. Poleć książkęKup książkę 2.1. Czym jest funkcja? 43 Z(cid:224)O(cid:297)ENIE FUNKCJI Funkcje to bloczki, które mo(cid:285)na po(cid:239)(cid:200)czy(cid:202) w celu zbudowania innych funkcji. Z(cid:239)o(cid:285)enie funkcji f i g zapisuje si(cid:218) jako f ° g, który czyta si(cid:218) f od g. Je(cid:258)li f(x) = x + 2 i g(x) = x * 2, wtedy: f ° g (x) = f(g(x)) = f(x * 2) = (x * 2) + 2 Zauwa(cid:285), (cid:285)e zapisy f ° g (x) i f(g(x)) s(cid:200) równowa(cid:285)ne. Jednak zapis kompozycji jako f(g(x)) wskazuje, (cid:285)e u(cid:285)ywa si(cid:218) x jako miejsca dla argumentu. Zapis f ° g pozwala na okre- (cid:258)lenie z(cid:239)o(cid:285)enia funkcji bez wskazywania elementu tymczasowego. Je(cid:258)li u(cid:285)yjemy tej funkcji dla warto(cid:258)ci 5, otrzymamy: f ° g (5) = f(g(5)) = f(5 * 2) = 10 + 2 = 12 Warto zwróci(cid:202) uwag(cid:218), (cid:285)e f ° g ró(cid:285)ni si(cid:218) od g ° f, cho(cid:202) czasem mog(cid:200) by(cid:202) sobie rów- nowa(cid:285)ne. Oto przyk(cid:239)ad: g ° f (5) = g(f(5)) = g(5 + 2) = 7 * 2 = 14 Funkcje stosuje si(cid:218) odwrotnie do kolejno(cid:258)ci zapisu. Zapis f ° g oznacza, (cid:285)e najpierw stosuje si(cid:218) g, a potem f. Standardowe funkcje Javy 8 definiuj(cid:200) metod(cid:218) compose() i metod(cid:218) andThen(), aby obs(cid:239)u(cid:285)y(cid:202) oba przypadki. W praktyce nie jest to potrzebne, bo f.andThen(g) oznacza g.compose(f) lub f ° g. FUNKCJE Z KILKOMA ARGUMENTAMI Na razie mówili(cid:258)my jedynie o funkcjach z jednym argumentem. A co z funkcjami posia- daj(cid:200)cymi kilka argumentów? Tak naprawd(cid:218) nie istnieje funkcja z kilkoma argumentami. Przypomn(cid:218) definicj(cid:218): funkcja to relacja pomi(cid:218)dzy zbiorem (cid:283)ród(cid:239)owym i zbiorem doce- lowym. Nie jest to relacja mi(cid:218)dzy kilkoma zbiorami wej(cid:258)ciowymi i zbiorem docelowym. Funkcja nie mo(cid:285)e mie(cid:202) kilku argumentów. Iloczyn dwóch zbiorów równie(cid:285) jest zbiorem, wi(cid:218)c funkcja korzystaj(cid:200)ca z takiego iloczynu zbiorów mo(cid:285)e si(cid:218) wydawa(cid:202) funkcj(cid:200) przyjmuj(cid:200)c(cid:200) kilka argumentów. Rozwa(cid:285)my nast(cid:218)puj(cid:200)c(cid:200) sytuacj(cid:218): f(x, y) = x + y Jest to relacja mi(cid:218)dzy N × N i N, czyli mamy do czynienia z funkcj(cid:200). Istnieje jednak tylko jeden argument — jest nim N × N. N × N to zbiór wszystkich mo(cid:285)liwych par liczb ca(cid:239)kowitych. Elementami takiego zbioru s(cid:200) pary liczb ca(cid:239)kowitych. Para to specjalny przypadek bardziej ogólnej koncepcji nazy- wanej krotk(cid:200), która reprezentuje po(cid:239)(cid:200)czenie kilku elementów. Para to dwuelementowa krotka. Krotki zapisuje si(cid:218) w nawiasach, wi(cid:218)c (3, 5) to krotka i element zbioru N × N. Mo(cid:285)emy dla tej krotki u(cid:285)y(cid:202) funkcji f: f((3, 5)) = 3 + 5 = 8 W takiej sytuacji przyjmuje si(cid:218), (cid:285)e zgodnie z konwencj(cid:200) jeden zestaw nawiasów jest zb(cid:218)dny, wi(cid:218)c powstaje zapis: Poleć książkęKup książkę 44 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java f(3, 5) = 3 + 5 = 8 Nadal jest to jednak funkcja z jedn(cid:200) krotk(cid:200), a nie funkcja z dwoma argumentami. ROZWIJANIE FUNKCJI Funkcje z krotkami mo(cid:285)emy potraktowa(cid:202) nieco inaczej. Funkcj(cid:218) f(5, 3) mo(cid:285)emy zde- finiowa(cid:202) jako funkcj(cid:218) z N do zbioru funkcji od N. Poprzedni przyk(cid:239)ad mogliby(cid:258)my wi(cid:218)c zapisa(cid:202) nast(cid:218)puj(cid:200)co: f(x)(y) = g(y) gdzie: g(y) = x + y W takiej sytuacji mo(cid:285)emy napisa(cid:202): f(x) = g Oznacza to, (cid:285)e wynikiem zastosowania funkcji f dla argumentu x jest nowa funkcja g. Zastosowanie funkcji g dla y daje wynik: g(y) = x + y W przypadku stosowania g, x nie jest ju(cid:285) dost(cid:218)pne. Nie zale(cid:285)y od argumentu lub czego- kolwiek innego. To sta(cid:239)a. Stosuj(cid:200)c to dla przyk(cid:239)adu z (3, 5), otrzymujemy: f(3)(5) = g(5) = 3 + 5 = 8 Nowym elementem jest jedynie to, (cid:285)e przeciwdziedzina f to zbiór funkcji, a nie zbiór liczb. Wynikiem zastosowania f dla liczby ca(cid:239)kowitej jest funkcja. Wynikiem zastosowania tej funkcji dla liczby ca(cid:239)kowitej jest liczba ca(cid:239)kowita. Posta(cid:202) f(x)(y) to rozwini(cid:218)ta forma funkcji f(x, y). Zastosowanie tego przekszta(cid:239)cenia dla funkcji krotki (je(cid:258)li chcesz, mo(cid:285)esz u(cid:285)y(cid:202) nazwy „funkcja wieloargumentowa”) nazywa si(cid:218) rozwini(cid:218)ciem funkcji (ang. currying). Wersja angielska nazwy pochodzi od nazwi- ska matematyka Haskella Curry’ego (cho(cid:202) to nie on wymy(cid:258)li(cid:239) to przekszta(cid:239)cenie). CZ(cid:265)(cid:285)CIOWO ZASTOSOWANA FUNKCJA Posta(cid:202) rozwini(cid:218)ta funkcji w postaci dodatkowej funkcji po(cid:258)redniej mo(cid:285)e nie wydawa(cid:202) si(cid:218) naturalna, wi(cid:218)c zapewne zastanawiasz si(cid:218), jak odnie(cid:258)(cid:202) j(cid:200) do czego(cid:258) ze (cid:258)wiata rzeczywi- stego — przecie(cid:285) w tej wersji ka(cid:285)dy z argumentów rozwa(cid:285)a si(cid:218) osobno. Najpierw obs(cid:239)u- guje si(cid:218) jeden argument, czego efektem jest nowa funkcja. Czy ta nowa funkcja jest u(cid:285)y- teczna sama z siebie, czy te(cid:285) stanowi jedynie krok w wi(cid:218)kszych obliczeniach? W przypadku dodawania faktycznie nie jest zbyt u(cid:285)yteczna. Tak przy okazji — mo(cid:285)na rozpocz(cid:200)(cid:202) od któregokolwiek z argumentów. Nie ma to znaczenia, bo cho(cid:202) funkcja po(cid:258)red- nia b(cid:218)dzie inna, wynik ko(cid:241)cowy nie ulegnie zmianie. Rozwa(cid:285)my now(cid:200) funkcj(cid:218) na parze warto(cid:258)ci: f(procent, cena) = cena / 100 * (100 + procent) Poleć książkęKup książkę 2.2. Funkcje w Javie 45 Funkcja ta jest równowa(cid:285)na funkcji: g(cena, procent) = cena / 100 * (100 + procent) Oto obie funkcje po rozwini(cid:218)ciu: f(procent, cena) g(cena, procent) Wiemy, (cid:285)e f i g to funkcje. Czym s(cid:200) jednak f(procent) i g(cena)? Z pewno(cid:258)ci(cid:200) s(cid:200) efek- tem zastosowania f dla procent i g dla cena. Jaki to jednak typ wyniku? f(procent) to funkcja zmiany z jednej ceny na inn(cid:200). Je(cid:258)li procent = 9, funkcja stosuje podatek wynosz(cid:200)cy 9 dla zadanej ceny, czym tworzy now(cid:200) cen(cid:218). Wynikow(cid:200) funkcj(cid:218) mo(cid:285)na by nazwa(cid:202) zastosujDziewi(cid:250)cioprocentowyPodatek(cena). By(cid:239)oby to dosy(cid:202) u(cid:285)yteczne narz(cid:218)dzie, je(cid:258)li stawka podatku nie zmienia si(cid:218) zbyt cz(cid:218)sto. Z drugiej strony, g(cena) to funkcja zmiany z warto(cid:258)ci procentowej na cen(cid:218). Je(cid:258)li cena wynosi 100 z(cid:239)otych, nowa funkcja zastosuje wskazan(cid:200) stawk(cid:218) podatku dla ceny wyno- sz(cid:200)cej 100 z(cid:239)otych. Jak nazwa(cid:239)by(cid:258) tak(cid:200) funkcj(cid:218)? Je(cid:258)li nie potrafisz wymy(cid:258)li(cid:202) sensownej nazwy, oznacza to zapewne, (cid:285)e taka posta(cid:202) nie ma sensu (cho(cid:202) wiele zale(cid:285)y od rodzaju rozwi(cid:200)zywanego problemu). Funkcje takie jak f(procent) i g(cena) nazywa si(cid:218) cz(cid:218)sto funkcjami zastosowanymi cz(cid:218)(cid:258)ciowo, aby odró(cid:285)ni(cid:202) je od wersji f(procent, cena) i g(cena, procent). Cz(cid:218)(cid:258)ciowo zastosowane funkcje mog(cid:200) mie(cid:202) du(cid:285)y wp(cid:239)yw na obs(cid:239)ug(cid:218) argumentu. Wrócimy do tego tematu w dalszej cz(cid:218)(cid:258)ci ksi(cid:200)(cid:285)ki. Je(cid:258)li masz problem ze zrozumieniem rozwijania funkcji, wyobra(cid:283) sobie, (cid:285)e podró- (cid:285)ujesz do innego kraju i masz przy sobie kalkulator (lub smartfon) pozwalaj(cid:200)cy na kon- wersj(cid:218) z jednej waluty na inn(cid:200). Czy chcia(cid:239)by(cid:258) wpisywa(cid:202) za ka(cid:285)dym razem warto(cid:258)(cid:202) prze- licznika, czy raczej zapisa(cid:239)by(cid:258) j(cid:200) w pami(cid:218)ci kalkulatora? Które z tych rozwi(cid:200)za(cid:241) by(cid:239)oby mniej nara(cid:285)one na b(cid:239)(cid:218)dy? FUNKCJE NIE MAJ(cid:260) EFEKTÓW Pami(cid:218)taj, (cid:285)e czyste funkcje jedynie zwracaj(cid:200) warto(cid:258)(cid:202) — nie maj(cid:200) (cid:285)adnych efektów ubocz- nych. Nie modyfikuj(cid:200) stanu (cid:285)adnego elementu ze (cid:258)wiata zewn(cid:218)trznego (przez (cid:258)wiat zewn(cid:218)trzny rozumiemy elementy poza sam(cid:200) funkcj(cid:200)), nie modyfikuj(cid:200) przekazanych argumentów i nie eksploduj(cid:200) (zg(cid:239)aszaj(cid:200) wyj(cid:200)tku), je(cid:258)li pojawi si(cid:218) b(cid:239)(cid:200)d. Mog(cid:200) jednak zwróci(cid:202) wyj(cid:200)tek jako wynik wraz z informacj(cid:200) o b(cid:239)(cid:218)dzie. Musz(cid:200) go jednak zwróci(cid:202), a nie zg(cid:239)osi(cid:202) (rzuci(cid:202)), umie(cid:258)ci(cid:202) w dzienniku zdarze(cid:241) lub wypisa(cid:202) na ekranie. 2.2. Funkcje w Javie W rozdziale 1. u(cid:285)ywa(cid:239)e(cid:258) czego(cid:258), co nazwa(cid:239)em funkcjami, cho(cid:202) tak naprawd(cid:218) by(cid:239)y to metody. Metody to sposób reprezentacji (do pewnego stopnia) funkcji w j(cid:218)zyku Java. 2.2.1. Metody funkcyjne Metoda mo(cid:285)e by(cid:202) funkcyjna, je(cid:258)li stosuje si(cid:218) do zasad obowi(cid:200)zuj(cid:200)cych czyste funkcje: Poleć książkęKup książkę 46 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java (cid:81) Nie mo(cid:285)e modyfikowa(cid:202) niczego poza funkcj(cid:200); (cid:285)adne zmiany wewn(cid:200)trz funkcji nie mog(cid:200) wycieka(cid:202) na zewn(cid:200)trz. (cid:81) Nie mo(cid:285)e modyfikowa(cid:202) argumentów. (cid:81) Nie mo(cid:285)e rzuca(cid:202) wyj(cid:200)tków lub b(cid:239)(cid:218)dów. (cid:81) Musi zawsze zwraca(cid:202) warto(cid:258)(cid:202). (cid:81) Po wywo(cid:239)aniu z tymi samymi argumentami musi zawsze zwróci(cid:202) ten sam wynik. Przyjrzyjmy si(cid:218) przyk(cid:239)adowi z listingu 2.1. Listing 2.1. Metody funkcyjne public class FunctionalMethods { public int percent1 = 5; private int percent2 = 9; public final int percent3 = 13; public int add(int a, Integer b) { return a + b; } public int mult(int a, Integer b) { a = 5; b = 2; return a * b; } public int div(int a, int b) { return a / b; } public int applyTax1(int a) { return a / 100 * (100 + percent1); } public int applyTax2(int a) { return a / 100 * (100 + percent2); } public int applyTax3(int a) { return a / 100 * (100 + percent3); } public List Integer append(int i, List Integer list) { list.add(i); return list; } public List Integer append2(int i, List Integer list) { List Integer result = new ArrayList (); result.add(i); percent2++; return result; } } Poleć książkęKup książkę 2.2. Funkcje w Javie 47 Czy mo(cid:285)esz wskaza(cid:202), które z tych metod reprezentuj(cid:200) czyste funkcje? Zastanów si(cid:218) nad tym kilka minut, zanim zaczniesz czyta(cid:202) odpowiedzi umieszczone poni(cid:285)ej. Pomy(cid:258)l o wszyst- kich warunkach i ca(cid:239)ym przetwarzaniu danych umieszczonym w ka(cid:285)dej z funkcji. Pami(cid:218)taj, (cid:285)e to, co ma znaczenie, dotyczy widoczno(cid:258)ci na zewn(cid:200)trz. Nie zapomnij o uwzgl(cid:218)dnie- niu wyj(cid:200)tków. Przyjrzyjmy si(cid:218) pierwszej metodzie: public int add(int a, int b) { return a + b; } Metoda add jest funkcj(cid:200), poniewa(cid:285) zawsze zwraca warto(cid:258)(cid:202), która zale(cid:285)y tylko od jej argu- mentów. Nie modyfikuje argumentów i nie wchodzi w (cid:285)aden sposób w interakcj(cid:218) ze (cid:258)wiatem zewn(cid:218)trznym. Metoda mo(cid:285)e spowodowa(cid:202) b(cid:239)(cid:200)d, je(cid:258)li suma a + b sprowokuje przepe(cid:239)nienie maksymalnej warto(cid:258)ci typu int. Nie zg(cid:239)osi jednak wyj(cid:200)tku. Wynikiem b(cid:218)dzie b(cid:239)(cid:218)dna warto(cid:258)(cid:202) (najcz(cid:218)(cid:258)ciej ujemna), ale to inny problem. Wynik musi by(cid:202) taki sam za ka(cid:285)dym razem, gdy funkcj(cid:218) wywo(cid:239)amy z tymi samymi argumentami. Nie ozna- cza to, (cid:285)e wynik musi by(cid:202) dok(cid:239)adny! DOK(cid:224)ADNO(cid:285)(cid:251) Termin dok(cid:239)adno(cid:258)(cid:202) sam w sobie niewiele mówi. Wskazuje jedy- nie, (cid:285)e odpowiada temu, czego oczekiwano. Aby powiedzie(cid:202), (cid:285)e co(cid:258) jest dok(cid:239)adnie, jak zaplanowano, trzeba zna(cid:202) intencj(cid:218) implementuj(cid:200)cego. Najcz(cid:218)(cid:258)ciej jednak znamy tylko nazw(cid:218) funkcji, która traktowana jako wyrocznia w tej sprawie mo(cid:285)e by(cid:202) (cid:283)ród(cid:239)em nieporozumienia. Przejd(cid:283)my do drugiej metody: public int mult(int a, Integer b) { a = 5; b = 2; return a * b; } Metoda mult jest funkcj(cid:200) czyst(cid:200) z tych samych powodów, co metoda add. Mo(cid:285)e to by(cid:202) dla niektórych nieco dziwne, poniewa(cid:285) wydaje si(cid:218) modyfikowa(cid:202) argumenty. Argumenty w metodach Javy s(cid:200) jednak przekazywane przez warto(cid:258)(cid:202), wi(cid:218)c zmiana ich warto(cid:258)ci wewn(cid:200)trz funkcji nie powoduje uwidocznienia tej zmiany na zewn(cid:200)trz. Metoda zawsze zwróci warto(cid:258)(cid:202) 10, co nie jest zbyt przydatne, szczególnie (cid:285)e nie zale(cid:285)y to od przekaza- nych argumentów, ale spe(cid:239)nia to wszystkie wymagania. Kilkukrotne wywo(cid:239)anie metody dla tych samych argumentów spowoduje zwrócenie tej samej warto(cid:258)ci. Tak przy okazji, metoda ta jest równowa(cid:285)na metodzie bez argumentów. To szcze- gólny przypadek funkcji: f(x) = 10. To sta(cid:239)a. Przejd(cid:283)my do metody div: public int div(int a, int b) { return a / b; } Poleć książkęKup książkę 48 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java Metoda div nie jest funkcj(cid:200) czyst(cid:200), poniewa(cid:285) zg(cid:239)osi wyj(cid:200)tek, je(cid:258)li nast(cid:200)pi próba dziele- nia przez 0. Aby uczyni(cid:202) j(cid:200) funkcj(cid:200), mogliby(cid:258)my testowa(cid:202) drugi parametr i zwraca(cid:202) pewn(cid:200) warto(cid:258)(cid:202), je(cid:258)li wynosi on 0. Zwrócon(cid:200) warto(cid:258)ci(cid:200) musia(cid:239)by by(cid:202) int, wi(cid:218)c trudno by(cid:239)oby znale(cid:283)(cid:202) jak(cid:200)(cid:258) sensown(cid:200) warto(cid:258)(cid:202), ale to inny problem. Przejd(cid:283)my do czwartej metody: public int percent1 = 5; public int applyTax1(int a) { return a / 100 * (100 + percent1); } Metoda applyTax1 wydaje si(cid:218) nie by(cid:202) funkcj(cid:200) czyst(cid:200), poniewa(cid:285) jej wynik zale(cid:285)y od war- to(cid:258)ci percent1, która jest publiczna i mo(cid:285)e by(cid:202) modyfikowana mi(cid:218)dzy dwoma wywo(cid:239)a- niami funkcji. W konsekwencji dwa wywo(cid:239)ania funkcji u(cid:285)ywaj(cid:200)ce tego samego argu- mentu mog(cid:200) zwróci(cid:202) ró(cid:285)ne wyniki. Zmienn(cid:200) percent1 mo(cid:285)na traktowa(cid:202) jako niejawny parametr, ale nie jest on wyliczany w tym samym momencie co argument metody. Nie b(cid:218)dzie to problem, je(cid:258)li warto(cid:258)(cid:202) percent1 b(cid:218)dzie w metodzie u(cid:285)yta tylko raz. Je(cid:258)li odczy- tasz j(cid:200) dwa razy, mo(cid:285)e ulec modyfikacji mi(cid:218)dzy operacjami odczytu. Je(cid:258)li mia(cid:239)aby zosta(cid:202) u(cid:285)yta dwa razy, nale(cid:285)a(cid:239)oby j(cid:200) odczyta(cid:202) i wpisa(cid:202) do zmiennej lokalnej. Po tej operacji applyTax1 by(cid:239)oby funkcj(cid:200) czyst(cid:200) dla krotki (a, percent1), ale nie dla samej warto(cid:258)ci a. Rozwa(cid:285)my metod(cid:218) applyTax2: private int percent2 = 9; public int applyTax2(int a) { return a / 100 * (100 + percent2); } Metoda applyTax2 w zasadzie nie ró(cid:285)ni si(cid:218) od poprzedniej. Wydawa(cid:202) by si(cid:218) mog(cid:239)o, (cid:285)e jest to funkcja, poniewa(cid:285) zmienna percent2 jest prywatna. Jej stan mo(cid:285)e jednak ulec zmianie dzi(cid:218)ki metodzie setPercent2. Poniewa(cid:285) dost(cid:218)p do percent2 ma miejsce tylko raz, mo(cid:285)na traktowa(cid:202) applyTax2 jako funkcj(cid:218) czyst(cid:200) dla krotki (a, percent2). Je(cid:258)li rozwa(cid:285)amy j(cid:200) w kontek(cid:258)cie samego a, nie jest funkcj(cid:200) czyst(cid:200). Przejd(cid:283)my do szóstej metody: public final int percent3 = 13; public int applyTax3(int a) { return a / 100 * (100 + percent3); } Metoda applyTax3 jest szczególna. Dla tego samego argumentu zawsze zwróci t(cid:218) sam(cid:200) warto(cid:258)(cid:202), poniewa(cid:285) zale(cid:285)y tylko do tego argumentu i w(cid:239)a(cid:258)ciwo(cid:258)ci finalnej percent3, która nie mo(cid:285)e ulec zmianie. Wydawa(cid:202) by si(cid:218) mog(cid:239)o, (cid:285)e nie jest to funkcja czysta, poniewa(cid:285) jej wynik nie zale(cid:285)y tylko do argumentu metody (wynik funkcji czystych musi zale(cid:285)e(cid:202) tylko od argumentu). Nie b(cid:218)dzie jednak sprzeczno(cid:258)ci, je(cid:258)li potraktujemy percent3 jako argument pomocniczy. W zasadzie ca(cid:239)(cid:200) klas(cid:218) mo(cid:285)na potraktowa(cid:202) jako jeden argument pomocniczy, poniewa(cid:285) metody maj(cid:200) dost(cid:218)p do wszystkich w(cid:239)a(cid:258)ciwo(cid:258)ci klasy. Poleć książkęKup książkę 2.2. Funkcje w Javie 49 To istotne spostrze(cid:285)enie. Wszystkie metody instancji mo(cid:285)na zast(cid:200)pi(cid:202) metodami sta- tycznymi poprzez dodanie argumentu o typie takim jak klasa, w której si(cid:218) znajduj(cid:200). Metod(cid:218) applyTax3 mo(cid:285)na wi(cid:218)c zapisa(cid:202) jako: public static int applyTax3(FunctionalMethods x, int a) { return a / 100 * 100 + x.percent3; } Metod(cid:218) t(cid:218) mo(cid:285)na wywo(cid:239)a(cid:202) z wn(cid:218)trza klasy, przekazuj(cid:200)c referencj(cid:218) do this jako argument, np. applyTax3(this, a). Mo(cid:285)na j(cid:200) te(cid:285) wywo(cid:239)a(cid:202) z zewn(cid:200)trz, poniewa(cid:285) jest publiczna. Wystarczy jedynie referencja do instancji klasy FunctionalMethods. Metoda applyTax3 jest wi(cid:218)c funkcj(cid:200) czyst(cid:200) dla krotki (this, a). Doszli(cid:258)my do ostatniej metody: public List Integer append(int i, List Integer list) { list.add(i); return list; } Metoda append modyfikuje argument przed jego zwróceniem, a zmiana jest widoczna na zewn(cid:200)trz funkcji, wi(cid:218)c nie jest to funkcja czysta. NOTACJA OBIEKTOWA KONTRA NOTACJA FUNKCYJNA Przedstawi(cid:239)em sytuacj(cid:218), w której metody instancji korzystaj(cid:200)ce z w(cid:239)a(cid:258)ciwo(cid:258)ci klasy mo(cid:285)na potraktowa(cid:202) tak, jakby instancja klasy by(cid:239)a ich niejawnym parametrem. Metody korzystaj(cid:200)ce z zawarto(cid:258)ci instancji mog(cid:200) zosta(cid:202) zamienione na metody statyczne, je(cid:258)li ich wcze(cid:258)niej niejawny parametr (instancj(cid:218)) przeka(cid:285)e si(cid:218) jawnie. Rozwa(cid:285)my klas(cid:218) Payment z rozdzia(cid:239)u 1.: public class Payment { public final CreditCard cc; public final int amount; public Payment(CreditCard cc, int amount) { this.cc = cc; this.amount = amount; } public Payment combine(Payment other) { if (cc.equals(other.cc)) { return new Payment(cc, amount + other.amount); } else { throw new IllegalStateException( Karty nie pasuj(cid:230) do siebie. ); } } } Metoda combine korzysta z pól cc i amount klasy j(cid:200) zawieraj(cid:200)cej. Z tego powodu nie mo(cid:285)e by(cid:202) ustatyczniona. Metoda stosuje zawieraj(cid:200)c(cid:200) j(cid:200) klas(cid:218) jako niejawny parametr. Gdyby jednak parametr uczyni(cid:202) jawnym, mogliby(cid:258)my przekszta(cid:239)ci(cid:202) metod(cid:218) na jej statyczny odpowiednik: Poleć książkęKup książkę 50 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java public class Payment { public final CreditCard cc; public final int amount; public Payment(CreditCard cc, int amount) { this.cc = cc; this.amount = amount; } public static Payment combine(Payment payment1, Payment payment2) { if (payment1.cc.equals(payment2.cc)) { return new Payment(payment1.cc, payment1.amount + payment2.amount); } else { throw new IllegalStateException( Karty nie pasuj(cid:230) do siebie. ); } } } Metoda statyczna daje pewno(cid:258)(cid:202), (cid:285)e w zastosowanym kodzie nie istnieje (cid:285)aden niechciany dost(cid:218)p do kontekstu zawieraj(cid:200)cego metod(cid:218). Zmienia to jednak sposób korzystania z metody. Wewn(cid:200)trz klasy metod(cid:218) statyczn(cid:200) mo(cid:285)na wywo(cid:239)a(cid:202), przekazuj(cid:200)c jej referencj(cid:218) this: Payment newPayment = combine(this, otherPayment); Je(cid:258)li metod(cid:218) wywo(cid:239)ujemy spoza klasy, trzeba u(cid:285)y(cid:202) nazwy klasy: Payment newPayment = Payment.combine(payment1, payment2); Ró(cid:285)nica jest niewielka, ale wszystko ulega zmianie, gdy trzeba po(cid:239)(cid:200)czy(cid:202) wywo(cid:239)ania metod. Je(cid:258)li musimy po(cid:239)(cid:200)czy(cid:202) kilka p(cid:239)atno(cid:258)ci, metoda instancji zapisana jako: public Payment combine(Payment payment) { if (this.cc.equals(payment.cc)) { return new Payment(this.cc, this.amount + payment.amount); } else { throw new IllegalStateException( Karty nie pasuj(cid:230) do siebie. ); } } mo(cid:285)e skorzysta(cid:202) z notacji obiektowej: Payment newPayment = p0.combine(p1).combine(p2).combine(p3); To znacznie bardziej przejrzysta wersja ni(cid:285): Payment newPayment = p0.combine(p1).combine(p2).combine(p3); Co wi(cid:218)cej, dodanie jeszcze jednej p(cid:239)atno(cid:258)ci jest znacznie (cid:239)atwiejsze w pierwszej z przed- stawionych sytuacji. 2.2.2. Interfejsy funkcyjne Javy i klasy anonimowe Metody mo(cid:285)na uczyni(cid:202) funkcyjnymi, ale brakuje im czego(cid:258), co pozwala(cid:239)oby im repre- zentowa(cid:202) funkcje w programowaniu funkcyjnym — nie mo(cid:285)na ich zmienia(cid:202) poza przypi- sywaniem argumentów. Nie mo(cid:285)na przekaza(cid:202) metody jako argumentu do innej metody. Poleć książkęKup książkę 2.2. Funkcje w Javie 51 W konsekwencji nie mo(cid:285)na tworzy(cid:202) kompozycji metod bez ich wykonywania — dopusz- czalne s(cid:200) tylko kompozycje wykona(cid:241) metod, ale nie samych metod. Metoda Javy nale(cid:285)y do klasy, w której zosta(cid:239)a zdefiniowana, i ca(cid:239)y czas tam pozostaje. Mo(cid:285)emy tworzy(cid:202) kompozycje metod, wywo(cid:239)uj(cid:200)c je z innych metod, ale trzeba to czyni(cid:202), pisz(cid:200)c program. Je(cid:258)li niezb(cid:218)dne s(cid:200) ró(cid:285)ne kompozycje w zale(cid:285)no(cid:258)ci od konkret- nych warunków, trzeba wszystko przewidzie(cid:202) w trakcie pisania kodu. Nie mo(cid:285)na napisa(cid:202) programu w taki sposób, aby sam zmienia(cid:239) si(cid:218) w trakcie wykonywania. Czy aby na pewno? Ale(cid:285) mo(cid:285)na! Czasami rejestruje si(cid:218) procedury obs(cid:239)ugi w trakcie wykonywania pro- gramu, aby obs(cid:239)u(cid:285)y(cid:202) konkretne przypadki. Procedury obs(cid:239)ugi trafiaj(cid:200) na list(cid:218), z której mog(cid:200) by(cid:202) usuni(cid:218)te. Nic te(cid:285) nie stoi na przeszkodzie, aby zmieni(cid:202) kolejno(cid:258)(cid:202) ich wykony- wania. W jaki sposób realizuje si(cid:218) to zadanie? U(cid:285)ywaj(cid:200)c klas zawieraj(cid:200)cych metody z w(cid:239)a- (cid:258)ciw(cid:200) obs(cid:239)ug(cid:200) zadania. W interfejsach graficznych bardzo cz(cid:218)sto u(cid:285)ywa si(cid:218) elementów nas(cid:239)uchuj(cid:200)cych kon- kretne zdarzenia, na przyk(cid:239)ad przemieszczenie kursora myszy, zmian(cid:218) rozmiaru okna czy pisanie tekstu. Kod obs(cid:239)uguj(cid:200)cy zdarzenia umieszcza si(cid:218) najcz(cid:218)(cid:258)ciej w klasach anoni- mowych implementuj(cid:200)cych konkretny interfejs. Dok(cid:239)adnie ten sam mechanizm warto wykorzysta(cid:202) do utworzenia funkcji. Przypu(cid:258)(cid:202)my, (cid:285)e tworzymy metod(cid:218), która potraja przekazan(cid:200) liczb(cid:218) ca(cid:239)kowit(cid:200). Najpierw musimy zdefiniowa(cid:202) interfejs z jedn(cid:200) metod(cid:200): public interface Function { int apply(int arg); } Nast(cid:218)pnie implementujemy t(cid:218) metod(cid:218), aby utworzy(cid:202) funkcj(cid:218): Function triple = new Function() { @Override public int apply(int arg) { return arg * 3; } }; Funkcj(cid:218) mo(cid:285)emy teraz zastosowa(cid:202) dla zadanego argumentu: System.out.println(triple.apply(2)); 6 Musz(cid:218) przyzna(cid:202), (cid:285)e nie jest to zbyt spektakularne. Stara, dobra metoda by(cid:239)aby z pewno- (cid:258)ci(cid:200) (cid:239)atwiejsza w u(cid:285)yciu. Je(cid:258)li chcemy wykona(cid:202) inn(cid:200) funkcj(cid:218), wystarczy post(cid:200)pi(cid:202) dok(cid:239)ad- nie tak samo: Function square = new Function() { @Override public int apply(int arg) { return arg * arg; } }; Idzie nam dobrze, ale jakie s(cid:200) tego zalety? Poleć książkęKup książkę 52 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java 2.2.3. Z(cid:225)o(cid:298)enie funkcji Je(cid:258)li potraktuje si(cid:218) funkcje jak metody, ich z(cid:239)o(cid:285)enie wydaje si(cid:218) proste: System.out.println(square.apply(triple.apply(2))); 36 To jednak nie jest z(cid:239)o(cid:285)enie funkcji. To z(cid:239)o(cid:285)enie zastosowa(cid:241) funkcji. Z(cid:239)o(cid:285)enie funkcji to operacja binarna na funkcjach, podobnie jak dodawanie to operacja binarna na liczbach. Mo(cid:285)emy z(cid:239)o(cid:285)y(cid:202) funkcje programowo, u(cid:285)ywaj(cid:200)c do tego metody: Function compose(final Function f1, final Function f2) { return new Function() { @Override public int apply(int arg) { return f1.apply(f2.apply(arg)); } }; } System.out.println(compose(triple, square).apply(3)); 27 Zapewne zaczynasz rozumie(cid:202), jak du(cid:285)e daje to mo(cid:285)liwo(cid:258)ci! Pozostaj(cid:200) jeszcze do rozwi(cid:200)- zania dwa du(cid:285)e problemy. Po pierwsze, funkcja mo(cid:285)e przyjmowa(cid:202) i zwraca(cid:202) tylko liczby ca(cid:239)kowite (typ int). Przyst(cid:200)pmy od razu do rozwi(cid:200)zania tej kwestii. 2.2.4. Funkcje polimorficzne Aby funkcja sta(cid:239)a si(cid:218) bardziej u(cid:285)yteczna, zmie(cid:241)my j(cid:200) na funkcj(cid:218) polimorficzn(cid:200) poprzez parametryzacj(cid:218) typów. W Javie zadanie to realizuj(cid:200) typy generyczne: public interface Function T, U { U apply(T arg); } Stosuj(cid:200)c nowy interfejs, wcze(cid:258)niejsze funkcje mo(cid:285)emy zapisa(cid:202) nast(cid:218)puj(cid:200)co: Function Integer, Integer triple = new Function Integer, Integer () { @Override public Integer apply(Integer arg) { return arg * 3; } }; Function Integer, Integer square = new Function Integer, Integer () { @Override public Integer apply(Integer arg) { return arg * arg; } }; Zauwa(cid:285), (cid:285)e zmienili(cid:258)my typ z int na Integer, poniewa(cid:285) int nie mo(cid:285)e by(cid:202) u(cid:285)ywane w kon- tek(cid:258)cie parametryzacji typów. Na szcz(cid:218)(cid:258)cie, automatyczne pakowanie i rozpakowywanie typów podstawowych czyni ca(cid:239)(cid:200) konwersj(cid:218) transparentn(cid:200). Poleć książkęKup książkę 2.2. Funkcje w Javie 53 (cid:251)WICZENIE 2.1 Napisz metod(cid:218) compose wykorzystuj(cid:200)c(cid:200) dwie nowe funkcje. UWAGA Odpowiedzi do (cid:202)wicze(cid:241) znajduj(cid:200) si(cid:218) tu(cid:285) po ka(cid:285)dym (cid:202)wiczeniu, wi(cid:218)c polecam prób(cid:218) zmierzenia si(cid:218) z (cid:202)wiczeniem przed zagl(cid:200)daniem do odpowiedzi. Odpowied(cid:283) znajdziesz równie(cid:285) w kodzie (cid:283)ród(cid:239)owym do(cid:239)(cid:200)czonym do ksi(cid:200)(cid:285)ki. Pierwsze (cid:202)wiczenie jest proste, ale niektóre pó(cid:283)niejsze b(cid:218)d(cid:200) naprawd(cid:218) trudne, wi(cid:218)c nie(cid:239)atwo b(cid:218)dzie si(cid:218) oprze(cid:202) pokusie zajrzenia do rozwi(cid:200)zania. Pami(cid:218)taj, (cid:285)e im wi(cid:218)cej zastanawiania si(cid:218) i poszukiwania rozwi(cid:200)zania, tym wi(cid:218)cej si(cid:218) nauczysz. ROZWI(cid:260)ZANIE (cid:251)WICZENIA 2.1 static Function Integer, Integer compose(Function Integer, Integer f1, Function Integer, Integer f2) { return new Function Integer, Integer () { @Override public Integer apply(Integer arg) { return f1.apply(f2.apply(arg)); } }; } Problem ze sk(cid:225)adaniem funkcji Z(cid:225)o(cid:298)enie funkcji to bardzo wa(cid:298)ny element, ale implementowanie go w Javie obarczone jest du(cid:298)ym ryzykiem. Z(cid:225)o(cid:298)enie kilku funkcji nie jest szkodliwe. Pomy(cid:286)l jednak o li(cid:286)cie 10 tysi(cid:266)cy funkcji i ich z(cid:225)o(cid:298)eniu do jednej funkcji. (Mo(cid:298)na to zrobi(cid:252) operacj(cid:261) zwini(cid:266)cia, o której wi(cid:266)cej napisz(cid:266) w rozdziale 3.). W programowaniu imperatywnym ka(cid:298)da z funkcji jest wyliczana, zanim wynik zostanie przekazany na wej(cid:286)cie nast(cid:266)pnej funkcji. W programowaniu funkcyjnym z(cid:225)o(cid:298)enie funkcji oznacza zbudowanie wynikowej funkcji bez wyliczania czegokolwiek. To w(cid:225)a(cid:286)nie ta cecha czyni ca(cid:225)y mechanizm wyj(cid:261)tkowo u(cid:298)ytecznym, bo jeszcze nic nie liczymy. W konsekwencji próba zastosowania takiego z(cid:225)o(cid:298)enia funkcji spowoduje wywo(cid:225)anie wielu osadzonych metod, co mo(cid:298)e zako(cid:276)czy(cid:252) si(cid:266) przepe(cid:225)nieniem bufora. Mo(cid:298)na to zademonstrowa(cid:252) za pomoc(cid:261) prostego przyk(cid:225)adu (wykorzystuj(cid:261)c lambdy, czyli funkcje anonimowe, które omó- wi(cid:266) w nast(cid:266)pnym punkcie). int fnum = 10_000; Function Integer, Integer g = x - x; Function Integer, Integer f = x - x + 1; for (int i = 0; i fnum; i++) { g = Function.compose(f, g); }; System.out.println(g.apply(0)); Przepe(cid:225)nienie bufora nast(cid:261)pi, gdy fnum osi(cid:261)gnie warto(cid:286)(cid:252) oko(cid:225)o 7500. Mam nadziej(cid:266), (cid:298)e nie dokonujesz sk(cid:225)adania tysi(cid:266)cy funkcji przy byle okazji, ale warto zdawa(cid:252) sobie z spraw(cid:266) z tego ograniczenia. 2.2.5. Upraszczanie kodu za pomoc(cid:261) funkcji anonimowych Drugim problemem, o którym wspomnia(cid:239)em, jest to, (cid:285)e funkcje zdefiniowane za pomoc(cid:200) klas anonimowych s(cid:200) ma(cid:239)o wygodne w stosowaniu. U(cid:285)ywaj(cid:200)c Javy od wersji 5. do 7., nie mo(cid:285)na nic z tym zrobi(cid:202). Na szcz(cid:218)(cid:258)cie, Java 8 wprowadzi(cid:239)a lambdy, czyli funkcje anonimowe. Poleć książkęKup książkę 54 ROZDZIA(cid:224) 2. U(cid:285)ycie funkcji w j(cid:218)zyku Java Funkcje anonimowe nie zmieniaj(cid:200) sposobu definiowania interfejsu Function, ale czy- ni(cid:200) implementacj(cid:218) znacznie przyjemniejsz(cid:200): Function Integer, Integer triple = x - x * 3; Function Integer, Integer square = x - x * x; Funkcje anonimowe to nie tylko uproszczenie sk(cid:239)adni. Maj(cid:200) bowiem pewne konsekwencje równie(cid:285) w kwestii kompilacji kodu. Jedn(cid:200) z g(cid:239)ównych ró(cid:285)nic mi(cid:218)dzy funkcjami anoni- mowymi a tradycyjnym sposobem pisania klas anonimowych jest to, (cid:285)e mo(cid:285)na pomin(cid:200)(cid:202) typy po prawej stronie znaku równo(cid:258)ci. Sta(cid:239)o si(cid:218) to mo(cid:285)liwe tylko dlatego, (cid:285)e w Javie 8 wprowadzono usprawnienia dotycz(cid:200)ce wnioskowania na temat typów. Do Javy 7 jedyne wnioskowanie co do typu by(cid:239)o mo(cid:285)liwe w sytuacji tworzenia (cid:239)a(cid:241)- cucha dereferencji identyfikatorów: System.out.println(); Nie musimy podawa(cid:202) typu dla out, bo Java potrafi sama go odgadn(cid:200)(cid:202). Gdyby tworzenie (cid:239)a(cid:241)cucha wywo(cid:239)a(cid:241) nie by(cid:239)o mo(cid:285)liwe, musieliby(cid:258)my napisa(cid:202): PrintStream out = System.out; out.println(); Java 7 wprowadzi(cid:239)a pewne drobne usprawnienie w postaci operatora : List String list = new ArrayList (); Nie trzeba ponawia(cid:202) parametru typu String w ArrayList, poniewa(cid:285) Java potrafi sama go wywnioskowa(cid:202) na podstawie deklaracji. Dok(cid:239)adnie taki sam proces zachodzi w funk- cjach anonimowych: Function Integer, Integer triple = x - x * 3; W przedstawionym przyk(cid:239)adzie Java potrafi wywnioskowa(cid:202) typ x. Nie zawsze jest to jednak mo(cid:285)liwe. Je(cid:285)eli Java zg(cid:239)osi informacj(cid:218), (cid:285)e nie potrafi odgadn(cid:200)(cid:202) typu, musisz zapi- sa(cid:202) go jawnie. Wymaga to u(cid:285)ycia nawiasów: Function Integer, Integer triple = (Integer x) - x * 3; OKRE(cid:285)LANIE TYPU FUNKCJI Cho(cid:202) Java 8 wprowadzi(cid:239)a funkcje anonimowe u(cid:239)atwiaj(cid:200)ce implementacj(cid:218) prawdziwych funkcji, nie zawiera (cid:285)adnego narz(cid:218)dzia upraszczaj(cid:200)cego pisanie typów funkcji. Typem funkcji z Integer na Integer jest: Function Integer, Integer Implementacj(cid:218) funkcji zapisuje si(cid:218) nast(cid:218)puj(cid:200)co: x - wyra(cid:318)enie By(cid:239)oby mi(cid:239)o, gdyby(cid:258)my mogli zastosowa(cid:202) takie samo uproszczenie dla typu, co pozwo- li(cid:239)oby na zapisanie ca(cid:239)o(cid:258)ci jako: Integer - Integer square = x - x * x; Niestety, takiego zapisu Java 8 nie dopuszcza. Nie mo(cid:285)na go te(cid:285) doda(cid:202) samodzielnie. Poleć książkęKup książkę 2.3. Zaawansowane funkcjonalno(cid:258)ci funkcji 55 (cid:251)WICZENIE 2.2 Napisz now(cid:200) wersj(cid:218) metody compose, która korzysta z funkcji anonimowych (lambdy). ROZWI(cid:260)ZANIE 2.2 Zamiana klas anonimowych na funkcje anonimowe jest bardzo prosta. Oto pierwsza wersja metody compose: static Function Integer, Integer compose(Function Integer, Integer f1, Function Integer, Integer f2) { return new Function Integer, Integer () { @Override public Integer apply(Integer arg) { return f1.apply(f2.apply(arg)); } }; } Wystarczy tylko zast(cid:200)pi(cid:202) warto(cid:258)(cid:202) zwracan(cid:200) przez metod(cid:218) compose argumentem metody apply klasy anonimowej, po której pojawia si(cid:218) operator strza(cid:239)ki (- ) oraz warto(cid:258)(cid:202) zwra- cana przez metod(cid:218) apply: static Function Integer, Integer compose(Function Integer, Integer f1, Function Integer, Integer f2) { return arg - f1.apply(f2.apply(arg)); } Nazwa argumentu jest ca(cid:239)kowicie dowolna. Rysunek 2.2 przedstawia ca(cid:239)y proces: Rysunek 2.2. Zast(cid:261)pienie klasy anonimowej funkcj(cid:261) anonimow(cid:261) 2.3. Zaawansowane funkcjonalno(cid:286)ci funkcji Przedstawi(cid:239)em, jak wykona(cid:202) funkcje apply i compose. Wskaza(cid:239)em, (cid:285)e funkcja mo(cid:285)e by(cid:202) reprezentowana przez metody lub przez obiekty. Nie odpowiedzia(cid:239)em jednak jeszcze na podstawowe pytanie: dlaczego obiekty funkcji s(cid:200) potrzebne? Czy
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Java. Programowanie funkcyjne
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: