Darmowy fragment publikacji:
Tytuł oryginału: Becoming Functional
Tłumaczenie: Lech Lachowski
ISBN: 978-83-283-0243-3
© 2015 Helion S.A.
Authorized Polish translation of the English edition of Becoming Functional, ISBN
9781449368173.
© 2014 Joshua Backfield.
This translation is published and sold by permission of O’Reilly Media, Inc., which owns
or controls all rights to publish and sell the same.
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)
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/pfukpk
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Pliki z przykładami omawianymi w książce można znaleźć pod adresem:
ftp://ftp.helion.pl/przyklady/pfukpk.zip
Printed in Poland.
• Kup książkę
• Poleć książkę
• Oceń książkę
• Księgarnia internetowa
• Lubię to! » Nasza społeczność
Spis tre(cid:316)ci
Przedmowa ......................................................................................7
Przegl(cid:241)d koncepcji programowania funkcyjnego
1. Wprowadzenie .............................................................................. 15
15
16
16
16
16
16
17
17
17
18
Typy funkcyjne
Funkcje czyste
Rekurencja
Zmienne niemutowalne
Ewaluacja nierygorystyczna
Instrukcje
Dopasowywanie do wzorca
Programowanie funkcyjne i wspó(cid:228)bie(cid:276)no(cid:264)(cid:232)
Podsumowanie
2. Typy funkcyjne ............................................................................... 19
19
22
22
Wprowadzenie do firmy XXY
Funkcje jako obiekty
Refaktoryzacja przy u(cid:276)yciu struktur if-else
Refaktoryzacja przy u(cid:276)yciu obiektów funkcji
do wyodr(cid:246)bniania pól
Funkcje anonimowe
Funkcje lambda
Domkni(cid:246)cia
Funkcje wy(cid:276)szego rz(cid:246)du
Refaktoryzacja funkcji get za pomoc(cid:241) j(cid:246)zyka Groovy
Podsumowanie
24
30
30
33
35
37
38
3
Kup książkęPoleć książkę 3. Funkcje czyste ................................................................................ 41
41
45
50
53
54
Dane wyj(cid:264)ciowe zale(cid:276)(cid:241) od danych wej(cid:264)ciowych
Oczyszczanie funkcji
Skutki uboczne
Podsumowanie
Przestawianie si(cid:246) na j(cid:246)zyk Groovy
4. Zmienne niemutowalne ................................................................59
59
65
71
Mutowalno(cid:264)(cid:232)
Niemutowalno(cid:264)(cid:232)
Podsumowanie
5. Rekurencja .....................................................................................73
74
77
80
Wprowadzenie do rekurencji
Rekurencja
Rekurencja ogonowa
Refaktoryzacja funkcji
countEnabledCustomersWithNoEnabledContacts
Podsumowanie
Wprowadzenie do j(cid:246)zyka Scala
81
83
84
6. Ewaluacje rygorystyczne i nierygorystyczne ...............................87
88
89
93
96
Ewaluacja rygorystyczna
Ewaluacja nierygorystyczna (leniwa)
Leniwo(cid:264)(cid:232) mo(cid:276)e stwarza(cid:232) problemy
Podsumowanie
7.
Instrukcje ........................................................................................99
100
Skok na g(cid:228)(cid:246)bok(cid:241) wod(cid:246)
Proste instrukcje
100
102
Instrukcje blokowe
104
Wszystko jest instrukcj(cid:241)
Podsumowanie
112
4
(cid:95)
Spis tre(cid:316)ci
Kup książkęPoleć książkę 8. Dopasowywanie do wzorca .........................................................113
113
115
118
120
122
124
Proste dopasowania
Proste wzorce
Wyodr(cid:246)bnianie listy
Wyodr(cid:246)bnianie obiektów
Konwersja na dopasowywanie do wzorca
Podsumowanie
9. Funkcyjne programowanie obiektowe ....................................... 125
125
127
129
132
Hermetyzacja statyczna
Obiekty jako kontenery
Kod jako dane
Podsumowanie
Od imperatywno(cid:264)ci do funkcyjno(cid:264)ci
10. Podsumowanie ............................................................................ 134
134
135
135
Wprowadzenie funkcji wy(cid:276)szego rz(cid:246)du
Konwersja istniej(cid:241)cych metod na funkcje czyste
Konwersja p(cid:246)tli na metody rekurencyjne
lub ogonoworekurencyjne
Konwersja zmiennych mutowalnych na niemutowalne
Co dalej?
Nowe wzorce projektowe
Przekazywanie komunikatów
dla osi(cid:241)gni(cid:246)cia wspó(cid:228)bie(cid:276)no(cid:264)ci
Wzorzec Opcja (rozszerzenie wzorca Pusty Obiekt)
Czysto(cid:264)(cid:232) metody singletona z zachowaniem obiektowo(cid:264)ci
Wszystko razem
Podsumowanie
136
136
136
137
137
137
138
139
147
Skorowidz .................................................................................... 149
Spis tre(cid:316)ci
(cid:95)
5
Kup książkęPoleć książkę6
(cid:95)
Spis tre(cid:316)ci
Kup książkęPoleć książkęROZDZIA(cid:292) 7.
Instrukcje
Kiedy my(cid:264)limy o instrukcji, mamy na my(cid:264)li co(cid:264) takiego jak Integer x = 1
lub val x = 1, gdzie ustawiana jest zmienna. Technicznie rzecz bior(cid:241)c, ewalu-
acja tego wiersza nie daje (cid:276)adnej warto(cid:264)ci. Co jednak, je(cid:264)li mieliby(cid:264)my ju(cid:276)
zdefiniowan(cid:241) zmienn(cid:241) i ustawialiby(cid:264)my j(cid:241) pó(cid:274)niej, na przyk(cid:228)ad za pomoc(cid:241)
instrukcji x = 1? Niektórzy ju(cid:276) wiedz(cid:241), (cid:276)e w j(cid:246)zykach C i Java ta instrukcja rze-
czywi(cid:264)cie zwraca warto(cid:264)(cid:232) 1, tak jak zosta(cid:228)o to przedstawione w listingu 7.1.
Listing 7.1. Prosta instrukcja przypisania
public class Test {
public static void main(String[] args) {
Integer x = 0;
System.out.println( X wynosi + (x = 1).toString());
}
}
Instrukcje w programowaniu funkcyjnym wprowadzaj(cid:241) koncepcj(cid:246) polegaj(cid:241)c(cid:241)
na tym, (cid:276)e ka(cid:276)dy wiersz kodu powinien mie(cid:232) warto(cid:264)(cid:232) zwracan(cid:241). J(cid:246)zyki
imperatywne takie jak Java zawieraj(cid:241) koncepcj(cid:246) operatora trójargumen-
towego (ang. ternary operator). Daje to struktur(cid:246) if-else, która przeprowadza
ewaluacj(cid:246) do pewnej warto(cid:264)ci. W listingu 7.2 zosta(cid:228)o przedstawione proste
u(cid:276)ycie operatora trójargumentowego.
Listing 7.2. Prosta instrukcja trójargumentowa
public class Test {
public static void main(String[] args) {
Integer x = 1;
System.out.println( X wynosi: + ((x 0) ? dodatnie : ujemne ));
}
}
99
Kup książkęPoleć książkęGdyby(cid:264)my mogli zrobi(cid:232) wi(cid:246)kszy u(cid:276)ytek z instrukcji, mogliby(cid:264)my zmniejszy(cid:232)
liczb(cid:246) posiadanych zmiennych. Je(cid:264)li ograniczymy liczb(cid:246) zmiennych, to zredu-
kujemy mo(cid:276)liwo(cid:264)ci ich mutowania, przez co zwi(cid:246)kszymy mo(cid:276)liwo(cid:264)(cid:232) wyko-
nywania procesów wspó(cid:228)bie(cid:276)nych oraz osi(cid:241)gni(cid:246)cia wi(cid:246)kszej funkcyjno(cid:264)ci!
Skok na g(cid:293)(cid:253)bok(cid:233) wod(cid:253)
Twój szef jest bardzo zadowolony z Twoich dokona(cid:254) w XXY. Jest naprawd(cid:246)
pod wra(cid:276)eniem programowania funkcyjnego i chce, aby(cid:264) dokona(cid:228) konwersji
z j(cid:246)zyka cz(cid:246)(cid:264)ciowo funkcyjnego na j(cid:246)zyk w pe(cid:228)ni funkcyjny. Nie powinno
to by(cid:232) trudne, poniewa(cid:276) przez kilka ostatnich rozdzia(cid:228)ów osi(cid:241)gn(cid:246)li(cid:264)my ju(cid:276)
do(cid:264)(cid:232) du(cid:276)y stopie(cid:254) funkcyjno(cid:264)ci.
Wybierzemy j(cid:246)zyk, który dzia(cid:228)a na maszynie wirtualnej Javy (ang. Java
Virtual Machine — JVM), aby nie wprowadza(cid:232) nowych technologii, takich
jak (cid:264)rodowisko uruchomieniowe LISP lub Erlang. Mogliby(cid:264)my równie(cid:276)
wybra(cid:232) j(cid:246)zyki takie jak Clojure lub Erjang, ale dla celów tej ksi(cid:241)(cid:276)ki u(cid:276)yjemy
j(cid:246)zyka Scala, który ma sk(cid:228)adni(cid:246) podobn(cid:241) jak Java i nie wymaga d(cid:228)ugiej nauki.
Proste instrukcje
Przepiszemy ka(cid:276)d(cid:241) z naszych klas, zacznijmy wi(cid:246)c od najprostszego pliku,
czyli klasy Contact. Przypomnijmy istniej(cid:241)cy plik w listingu 7.3.
Listing 7.3. Plik Contact.groovy
public class Contact {
public final Integer contact_id = 0;
public final String firstName = ;
public final String lastName = ;
public final String email = ;
public final Boolean enabled = true;
public Contact(Integer contact_id,
String firstName,
String lastName,
String email,
Boolean enabled) {
this.contact_id = contact_id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.enabled = enabled;
}
100
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkę public static List Customer setNameAndEmailForContactAndCustomer(
Integer customer_id,
Integer contact_id,
String name,
String email) {
Customer.updateContactForCustomerContact(
customer_id,
contact_id,
{ contact -
new Contact(
contact.contact_id,
contact.firstName,
name,
email,
contact.enabled
)
}
)
}
public void sendEmail() {
println( Wysy(cid:239)anie wiadomo(cid:258)ci e-mail )
}
}
Zrefaktoryzujemy ten kod na odpowiednik w j(cid:246)zyku Scala, tak jak zosta(cid:228)o
to przedstawione w listingu 7.4. Zwró(cid:232) uwag(cid:246), (cid:276)e w kodzie w j(cid:246)zyku Scala
definiujemy zmienne instancji w zestawie nawiasów obok nazwy klasy.
Mamy równie(cid:276) obiekt i klas(cid:246). Statyczne metody i sk(cid:228)adowe znajduj(cid:241) si(cid:246)
wewn(cid:241)trz definicji obiektu, a nie klasy. Typy definiowane s(cid:241) tak(cid:276)e raczej po
niej, a nie przed ni(cid:241).
Listing 7.4. Plik Contact.scala
object Contact {
def setNameAndEmailForContactAndCustomer(
customer_id : Integer,
contact_id : Integer,
name : String,
email : String) : List[Customer] = {
Customer.updateContactForCustomerContact(
customer_id,
contact_id,
{ contact =
new Contact(
contact.contact_id,
contact.firstName,
name,
email,
contact.enabled
)
}
Proste instrukcje
(cid:95)
101
Kup książkęPoleć książkę )
}
}
class Contact(val contact_id : Integer,
val firstName : String,
val lastName : String,
val email : String,
val enabled : Boolean) {
def sendEmail() = {
println( Wysy(cid:239)anie wiadomo(cid:258)ci e-mail )
}
}
Chocia(cid:276) dla czytelno(cid:264)ci w tej ksi(cid:241)(cid:276)ce dodawanych jest wiele wier-
szy, w tym wierszy pustych i definicji metod podzielonych na
kilka wierszy, liczba linii kodu spada z 19 do 9. Wynika to ze
sposobu, w jaki w j(cid:246)zyku Java definiujemy sk(cid:228)adowe i ustawiamy
je za pomoc(cid:241) konstruktora.
Instrukcje blokowe
Kolejn(cid:241) klas(cid:241), z któr(cid:241) si(cid:246) zmierzymy, jest Contract. Jest to nieco trudniejsze,
poniewa(cid:276) u(cid:276)ywali(cid:264)my obiektu Javy Calendar, który nie jest konstruktem zbyt
funkcyjnym. Rzu(cid:232)my okiem na oryginalny plik w listingu 7.5.
Listing 7.5. Plik Contract.groovy
import java.util.List;
import java.util.Calendar;
public class Contract {
public final Calendar begin_date;
public final Calendar end_date;
public final Boolean enabled = true;
public Contract(Calendar begin_date, Calendar end_date, Boolean enabled) {
this.begin_date = begin_date;
this.end_date = end_date;
this.enabled = enabled;
}
public Contract(Calendar begin_date, Boolean enabled) {
this.begin_date = begin_date;
this.end_date = this.begin_date.getInstance();
this.end_date.setTimeInMillis(this.begin_date.getTimeInMillis());
this.end_date.add(Calendar.YEAR, 2);
this.enabled = enabled;
}
102
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkę public static List Customer setContractForCustomerList(
List Integer ids,
Boolean status) {
Customer.updateContractForCustomerList(ids) { contract -
new Contract(contract.begin_date, contract.end_date, status)
}
}
}
Przejd(cid:274)my dalej i przekonwertujmy t(cid:246) klas(cid:246), tak jak zosta(cid:228)o to przedstawione
w listingu 7.6. Spójrzmy najpierw na fragment List[Integer], który przed-
stawia sposób oznaczania typizowania uogólnionego w Scali. Widzimy rów-
nie(cid:276) bardzo interesuj(cid:241)c(cid:241) sk(cid:228)adni(cid:246) def this(begin_date : Calendar, enabled :
(cid:180)Boolean), za pomoc(cid:241) której definiujemy konstruktor alternacyjny. Istnieje
tak(cid:276)e wiersz, który zawiera tylko warto(cid:264)(cid:232) c. To poprawne, gdy(cid:276) wiersz ten
traktowany jest jako instrukcja, czyli uznawany jest nast(cid:246)pnie za warto(cid:264)(cid:232)
zwracan(cid:241) tego bloku kodu.
Listing 7.6. Plik Contract.scala
import java.util.Calendar
object Contract {
def setContractForCustomerList(ids : List[Integer],
status : Boolean) : List[Customer] = {
Customer.updateContractForCustomerList(ids, { contract =
new Contract(contract.begin_date, contract.end_date, status)
})
}
}
class Contract(val begin_date : Calendar,
val end_date : Calendar,
val enabled : Boolean) {
def this(begin_date : Calendar, enabled : Boolean) = this(begin_date, {
val c = Calendar.getInstance()
c.setTimeInMillis(begin_date.getTimeInMillis)
c.add(Calendar.YEAR, 2)
c
}, enabled)
}
Najbardziej interesuj(cid:241)ce w tej sk(cid:228)adni jest wywo(cid:228)anie s(cid:228)owa kluczowego this,
w którym przekazujemy to, co zdaje si(cid:246) by(cid:232) funkcj(cid:241), tam, gdzie przeka-
zywana powinna by(cid:232) zmienna end_date. Dlaczego kompilator nie narzeka,
(cid:276)e oczekiwana jest instancja Calendar, a nie metoda, która zwraca instancj(cid:246)
Calendar?
Kompilator inferuje, (cid:276)e nie przekazujesz metody, ale zamiast tego chcesz prze-
prowadzi(cid:232) ewaluacj(cid:246) nawiasów {...}. Dlatego gdy wywo(cid:228)any jest konstruktor
Instrukcje blokowe
(cid:95)
103
Kup książkęPoleć książkęalternacyjny, wywo(cid:228)ujemy rzeczywisty konstruktor, a ewaluacja nawiasów
{...} daje nam end_date typu Calendar. Konstruktory alternacyjne dzia(cid:228)aj(cid:241)
w podobny sposób, w jaki Java pozwala przeci(cid:241)(cid:276)a(cid:232) konstruktory, aby
przyjmowa(cid:228)y ró(cid:276)ne argumenty.
Blok kodu przedstawiony w listingu 7.7 jest bardzo prosty. Tworzy obiekt
Calendar, ustawiaj(cid:241)c czas w milisekundach na podstawie obiektu begin_date
(przypomina to domkni(cid:246)cie). Nast(cid:246)pnie do daty dodawane s(cid:241) dwa lata,
aby utworzy(cid:232) dat(cid:246) dwa lata pó(cid:274)niejsz(cid:241) wobec momentu zawarcia kontraktu.
Na koniec zwracany jest nowo utworzony obiekt c, zawieraj(cid:241)cy dat(cid:246) dwa
lata pó(cid:274)niejsz(cid:241) od daty pocz(cid:241)tkowej begin_date.
Listing 7.7. Blok kodu okre(cid:264)laj(cid:241)cy warto(cid:264)(cid:232) dla end_date
{
val c = Calendar.getInstance()
c.setTimeInMillis(begin_date.getTimeInMillis)
c.add(Calendar.YEAR, 2)
c
}
Ta instrukcja pozwala nam wyj(cid:264)(cid:232) poza standardowy paradygmat funkcyjny,
w którym ka(cid:276)da linia kodu powinna by(cid:232) instrukcj(cid:241) mo(cid:276)liw(cid:241) do bezpo(cid:264)red-
niego przekazania do innej funkcji lub u(cid:276)ycia. Mo(cid:276)na traktowa(cid:232) to jako
instrukcj(cid:246) z(cid:228)o(cid:276)on(cid:241): mamy kilka instrukcji, które musz(cid:241) by(cid:232) poddane ewalu-
acji, aby uzyska(cid:232) faktycznie wykorzystywan(cid:241) instrukcj(cid:246) ogóln(cid:241).
Ten blok kodu jest interesuj(cid:241)cy, poniewa(cid:276) pokazuje, (cid:276)e ca(cid:228)kiem dos(cid:228)ownie
wszystko jest instrukcj(cid:241). Ostatni wiersz (c) jest instrukcj(cid:241), gdy(cid:276) zwraca
zmienn(cid:241) c. Tak(cid:276)e ca(cid:228)y blok kodu jest sam w sobie instrukcj(cid:241): po poddaniu
ewaluacji wykonuje linie kodu w sekwencji i zwraca now(cid:241) warto(cid:264)(cid:232) c, któr(cid:241)
zdefiniowali(cid:264)my.
Wszystko jest instrukcj(cid:233)
W ko(cid:254)cu zamierzamy przekonwertowa(cid:232) klas(cid:246) Customer, co nie powinno
by(cid:232) zbyt trudne. Spójrzmy na oryginalny plik Groovy przedstawiony w lis-
tingu 7.8.
Listing 7.8. Plik Customer.groovy
import java.util.ArrayList;
import java.util.List;
import java.util.Calendar;
104
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkępublic class Customer {
static public List Customer allCustomers = new ArrayList Customer ();
public final Integer id = 0;
public final String name = ;
public final String state = ;
public final String domain = ;
public final Boolean enabled = true;
public final Contract contract = null;
public final List Contact contacts = new ArrayList Contact ();
@Lazy public List Contact enabledContacts = contacts.findAll { contact -
contact.enabled
}
public Customer(Integer id,
String name,
String state,
String domain,
Boolean enabled,
Contract contract,
List Contact contacts) {
this.id = id;
this.name = name;
this.state = state;
this.domain = domain;
this.enabled = enabled;
this.contract = contract;
this.contacts = contacts;
}
static def EnabledCustomer = { customer - customer.enabled == true }
static def DisabledCustomer = { customer - customer.enabled == false }
public static List String getDisabledCustomerNames() {
Customer.allCustomers.findAll(DisabledCustomer).collect({customer -
customer.name
})
}
public static List String getEnabledCustomerStates() {
Customer.allCustomers.findAll(EnabledCustomer).collect({customer -
customer.state
})
}
public static List String getEnabledCustomerDomains() {
Customer.allCustomers.findAll(EnabledCustomer).collect({customer -
customer.domain
})
}
public static List String getEnabledCustomerSomeoneEmail(String someone) {
Customer.allCustomers.findAll(EnabledCustomer).collect({customer -
Wszystko jest instrukcj(cid:233)
(cid:95)
105
Kup książkęPoleć książkę someone + @ + customer.domain
})
}
public static ArrayList Customer getCustomerById(
ArrayList Customer inList,
final Integer id) {
inList.findAll({customer - customer.id == id })
}
public static void eachEnabledContact(Closure cls) {
Customer.allCustomers.findAll { customer -
customer.enabled customer.contract.enabled
}.each { customer -
customer.contacts.each(cls)
}
}
public static List Customer updateCustomerByIdList(
List Customer initialIds,
List Integer ids,
Closure cls) {
if(ids.size() = 0) {
initialIds
} else if(initialIds.size() = 0) {
[]
} else {
def idx = ids.indexOf(initialIds[0].id)
def cust = idx = 0 ? cls(initialIds[0]) : initialIds[0]
[cust] + updateCustomerByIdList(
initialIds.drop(1),
idx = 0 ? ids.minus(initialIds[0].id) : ids,
cls
)
}
}
public static List Customer updateContactForCustomerContact(
Integer id,
Integer contact_id,
Closure cls) {
updateCustomerByIdList(Customer.allCustomers, [id], { customer -
new Customer(
customer.id,
customer.name,
customer.state,
customer.domain,
customer.enabled,
customer.contract,
customer.contacts.collect { contact -
if(contact.contact_id == contact_id) {
cls(contact)
} else {
106
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkę contact
}
}
)
})
}
public static List Customer updateContractForCustomerList(
List Integer ids,
Closure cls) {
updateCustomerByIdList(Customer.allCustomers, ids, { customer -
new Customer(
customer.id,
customer.name,
customer.state,
customer.domain,
customer.enabled,
cls(customer.contract),
customer.contacts
)
})
}
public static def countEnabledCustomersWithNoEnabledContacts = {
List Customer customers, Integer sum -
if(customers.isEmpty()) {
return sum
} else {
int addition = (customers.head().enabled
(customers.head().contacts.find({ contact -
contact.enabled
}) == null)) ? 1 : 0
return countEnabledCustomersWithNoEnabledContacts.trampoline(
customers.tail(),
addition + sum
)
}
}.trampoline()
}
Kiedy konwertujemy t(cid:246) klas(cid:246) i obiekt na j(cid:246)zyk Scala (patrz: listing 7.9), jedna
rzecz nie dzia(cid:228)a: nie ma operatora trójargumentowego! Przypomnij sobie
konstrukcj(cid:246) (warunek) ? true : false ?. Jak wida(cid:232) w pliku Scali, zast(cid:241)pili(cid:264)my
j(cid:241) prawdziw(cid:241) instrukcj(cid:241) if.
Listing 7.9. Plik Customer.scala
object Customer {
val allCustomers = List[Customer]()
def EnabledCustomer(customer : Customer) : Boolean = customer.enabled == true
Wszystko jest instrukcj(cid:233)
(cid:95)
107
Kup książkęPoleć książkę def DisabledCustomer(customer : Customer) : Boolean = customer.enabled ==
(cid:180)false
def getDisabledCustomerNames() : List[String] = {
Customer.allCustomers.filter(DisabledCustomer).map({ customer =
customer.name
})
}
def getEnabledCustomerStates() : List[String] = {
Customer.allCustomers.filter(EnabledCustomer).map({ customer =
customer.state
})
}
def getEnabledCustomerDomains() : List[String] = {
Customer.allCustomers.filter(EnabledCustomer).map({ customer =
customer.domain
})
}
def getEnabledCustomerSomeoneEmail(someone : String) : List[String] = {
Customer.allCustomers.filter(EnabledCustomer).map({ customer =
someone + @ + customer.domain
})
}
def getCustomerById(inList : List[Customer],
customer_id : Integer) : List[Customer] = {
inList.filter(customer = customer.customer_id == customer_id)
}
def eachEnabledContact(cls : Contact = Unit) {
Customer.allCustomers.filter({ customer =
customer.enabled customer.contract.enabled
}).foreach({ customer =
customer.contacts.foreach(cls)
})
}
def updateCustomerByIdList(initialIds : List[Customer],
ids : List[Integer],
cls : Customer = Customer) : List[Customer] = {
if(ids.size = 0) {
initialIds
} else if(initialIds.size = 0) {
List()
} else {
val precust = initialIds.find(cust = cust.customer_id == ids(0))
val cust = if(precust.isEmpty) { List() } else { List(cls(precust.get)) }
cust ::: updateCustomerByIdList(
initialIds.filter(cust = cust.customer_id == ids(0)),
ids.drop(1),
108
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkę cls
)
}
}
def updateContactForCustomerContact(customer_id : Integer,
contact_id : Integer,
cls : Contact = Contact) :
(cid:180)List[Customer] = {
updateCustomerByIdList(Customer.allCustomers, List(customer_id), { customer =
new Customer(
customer.customer_id,
customer.name,
customer.state,
customer.domain,
customer.enabled,
customer.contract,
customer.contacts.map { contact =
if(contact.contact_id == contact_id) {
cls(contact)
} else {
contact
}
}
)
})
}
def updateContractForCustomerList(ids : List[Integer],
cls : Contract = Contract) :
(cid:180)List[Customer] = {
updateCustomerByIdList(Customer.allCustomers, ids, { customer =
new Customer(
customer.customer_id,
customer.name,
customer.state,
customer.domain,
customer.enabled,
cls(customer.contract),
customer.contacts
)
})
}
def countEnabledCustomersWithNoEnabledContacts(customers : List[Customer],
sum : Int) : Integer = {
if(customers.isEmpty) {
sum
} else {
val addition = if(customers.head.enabled
customers.head.contacts.exists({ contact =
contact.enabled
})) {
Wszystko jest instrukcj(cid:233)
(cid:95)
109
Kup książkęPoleć książkę 1
} else {
0
}
countEnabledCustomersWithNoEnabledContacts(customers.tail, addition + sum)
}
}
}
class Customer(val customer_id : Integer,
val name : String,
val state : String,
val domain : String,
val enabled : Boolean,
val contract : Contract,
val contacts : List[Contact]) {
}
Scala nie zawiera koncepcji trójargumentowych, poniewa(cid:276) wszystko jest ju(cid:276)
instrukcj(cid:241). Oznacza to, (cid:276)e ewaluacja instrukcji if da jak(cid:241)(cid:264) warto(cid:264)(cid:232). Mo(cid:276)emy
napisa(cid:232) if(warunek) { true } else { false }, a ewaluacja instrukcji if da
nam warto(cid:264)(cid:232) true lub false.
Spójrzmy teraz na kod w listingu 7.10, który przedstawia sposób, w jaki
mo(cid:276)emy ustawi(cid:232) zmienn(cid:241) na podstawie instrukcji if.
Listing 7.10. Zwrócony rezultat instrukcji if
val addition = if(customers.head.enabled
customers.head.contacts.exists({ contact = contact.enabled })) {
1
} else {
0
}
Jak wida(cid:232), zmienna addition otrzyma warto(cid:264)(cid:232) 1 lub 0 w zale(cid:276)no(cid:264)ci od ewalu-
acji instrukcji if. Dlaczego jest to o wiele bardziej interesuj(cid:241)ce ni(cid:276) operator
trójargumentowy? Dlatego, (cid:276)e w tym przypadku if dzia(cid:228)a jak normalna
instrukcja if, co oznacza, i(cid:276) mo(cid:276)na doda(cid:232) dowoln(cid:241) ilo(cid:264)(cid:232) kodu wewn(cid:241)trz
sekcji true lub false instrukcji if. Operator trójargumentowy tak naprawd(cid:246)
dopuszcza stosowanie tylko bardzo prostych wyra(cid:276)e(cid:254), takich jak warto(cid:264)(cid:232)
lub podstawowe wywo(cid:228)anie metody.
Co jednak tak naprawd(cid:246) znaczy stwierdzenie „wszystko jest instrukcj(cid:241)”?
Oznacza to, (cid:276)e wszystko powinno ewaluowa(cid:232) do jakiej(cid:264) warto(cid:264)ci. Ale co to
dok(cid:228)adnie znaczy? Wielu z nas zna standardow(cid:241) metodologi(cid:246) ziarna (ang.
bean) w j(cid:246)zyku Java, która polega na posiadaniu zmiennej sk(cid:228)adowej z meto-
dami zwracaj(cid:241)cymi i ustawiaj(cid:241)cymi. Oczywi(cid:264)cie metoda zwracaj(cid:241)ca zwraca
jak(cid:241)(cid:264) warto(cid:264)(cid:232), ale co z metod(cid:241) ustawiaj(cid:241)c(cid:241)? Rzu(cid:232)my okiem na listing 7.11.
110
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkęListing 7.11. Metoda ustawiaj(cid:241)ca dla pola Foo w klasie Bar, która zwraca sam obiekt
public class Bar {
public Bar setFoo(Foo foo) { this.foo = foo; return this; }
public Foo getFoo() { return this.foo; }
}
Umo(cid:276)liwia to (cid:228)a(cid:254)cuchowanie wywo(cid:228)a(cid:254) funkcji i ustawianie kilku sk(cid:228)ado-
wych w jednym wierszu, tak jak zosta(cid:228)o to przedstawione w listingu 7.12.
Ale dlaczego chcemy to zrobi(cid:232)? Po prostu w ten sposób mo(cid:276)emy przedefi-
niowa(cid:232) metody ustawiaj(cid:241)ce i utworzy(cid:232) zmienne niemutowalne. Dlaczego?
Poniewa(cid:276) wewn(cid:241)trz metod ustawiaj(cid:241)cych mo(cid:276)emy utworzy(cid:232) now(cid:241) instancj(cid:246)
Bar z now(cid:241) warto(cid:264)ci(cid:241) i zwróci(cid:232) j(cid:241)! Oznacza to, (cid:276)e implementacja zmiennych
niemutowalnych staje si(cid:246) prostsza.
Listing 7.12. Metoda (cid:228)a(cid:254)cuchowania w obiekcie Bar
return bar.setFoo(newFoo).setBaz(newBaz).setQux(newQux);
A co z elementami takimi jak p(cid:246)tle for — czy to te(cid:276) s(cid:241) instrukcje? W(cid:228)a(cid:264)ciwie
tak, ale nie w taki sposób jak mo(cid:276)na sobie wyobra(cid:276)a(cid:232). P(cid:246)tle for przyjmuj(cid:241)
na ogó(cid:228) dwie postacie: normalnej p(cid:246)tli i wyra(cid:276)enia (ang. comprehension).
Pierwszy typ p(cid:246)tli zosta(cid:228) przedstawiony w listingu 7.13.
Listing 7.13. Przyk(cid:228)ad podstawowej p(cid:246)tli for w j(cid:246)zyku Scala
val x = for(i - 0 until 10) {
println(i)
}
Uruchomienie tego kodu powoduje wy(cid:264)wietlenie na ekranie liczb od 0 do 9.
Co wa(cid:276)niejsze, dla zmiennej x ustawiana jest jaka(cid:264) warto(cid:264)(cid:232) — w tym przy-
padku jest to warto(cid:264)(cid:232) Unit.
Mo(cid:276)e si(cid:246) to wydawa(cid:232) dziwne, ale w j(cid:246)zyku Scala Unit jest w(cid:228)a(cid:264)ciwie typem
void (czyli nie ma faktycznego typu). Oznacza to, (cid:276)e ewaluacja naszej p(cid:246)tli
for w rzeczywisto(cid:264)ci nie zwróci(cid:228)a (cid:276)adnej warto(cid:264)ci. Czym wi(cid:246)c s(cid:241) wyra(cid:276)enia?
Przyjrzyjmy si(cid:246) wyra(cid:276)eniu for w listingu 7.14.
Listing 7.14. Podstawowe wyra(cid:276)enie for w j(cid:246)zyku Scala
val x = for(i - 0 until 10) yield {
i*2
}
Wszystko jest instrukcj(cid:233)
(cid:95)
111
Kup książkęPoleć książkęMamy zmienn(cid:241) x, która jest list(cid:241) parzystych liczb z zakresu od 0 do 18.
Wyra(cid:276)enie pozwala nam wygenerowa(cid:232) now(cid:241) list(cid:246) jakich(cid:264) elementów lub
czasem iterowa(cid:232) przez inn(cid:241) list(cid:246). Spójrzmy na listing 7.15, w którym fak-
tycznie przeprowadzamy iteracj(cid:246) przez inn(cid:241) list(cid:246).
Listing 7.15. Wyra(cid:276)enie for dla innej listy w j(cid:246)zyku Scala
val x = for(i - List(1,2,3,4)) yield {
i*2
}
Jaka jest wi(cid:246)c ró(cid:276)nica mi(cid:246)dzy tym a wykorzystaniem dla listy funkcji map?
Przyjrzyjmy si(cid:246) listingowi 7.16. Ta funkcjonalno(cid:264)(cid:232) jest taka sama jak wyra-
(cid:276)enie for przedstawione w listingu 7.15.
Listing 7.16. Wywo(cid:228)anie map dla listy w j(cid:246)zyku Scala
val x = List(1,2,3,4).map({ i = i*2 })
W takim razie kiedy nale(cid:276)y u(cid:276)y(cid:232) funkcji map, a kiedy wyra(cid:276)enia? Zasadniczo
funkcja map jest dobra, je(cid:264)li masz ju(cid:276) list(cid:246) i musisz przeprowadzi(cid:232) na niej
operacj(cid:246). Wyra(cid:276)enia for sprawdzaj(cid:241) si(cid:246), je(cid:264)li budujemy list(cid:246) lub chcemy prze-
prowadzi(cid:232) okre(cid:264)lon(cid:241) operacj(cid:246) n razy.
Podsumowanie
Po(cid:264)wi(cid:246)cili(cid:264)my nieco czasu na przeprowadzenie migracji z j(cid:246)zyka Java do
j(cid:246)zyka Scala, podkre(cid:264)laj(cid:241)c nasze przej(cid:264)cie na j(cid:246)zyk funkcyjny, z którego
b(cid:246)dziemy mogli korzysta(cid:232) w kolejnych rozdzia(cid:228)ach. Instrukcje pozwalaj(cid:241)
zredukowa(cid:232) niektóre podstawowe fragmenty kodu, a czasem s(cid:241) konieczne,
aby nadal korzysta(cid:232) z okre(cid:264)lonych paradygmatów ziarna Javy. Na przyk(cid:228)a-
dach takich jak obiekt Calendar zobaczyli(cid:264)my, (cid:276)e gdy musimy u(cid:276)y(cid:232) metod
ustawiaj(cid:241)cych, mo(cid:276)emy utworzy(cid:232) instrukcje bloku, aby skonfigurowa(cid:232) obiekt
Calendar.
Instrukcje pokazuj(cid:241) nam równie(cid:276), (cid:276)e ka(cid:276)da metoda (nawet metody usta-
wiaj(cid:241)ce) powinna mie(cid:232) jak(cid:241)(cid:264) form(cid:246) warto(cid:264)ci zwracanej. Je(cid:264)li mamy metody
ustawiaj(cid:241)ce, które s(cid:241) instrukcjami, mo(cid:276)emy (cid:228)atwiej implementowa(cid:232) zmienne
niemutowalne. Dzi(cid:246)ki instrukcjom nasz kod jest te(cid:276) bardziej zwi(cid:246)z(cid:228)y, ponie-
wa(cid:276) zmuszaj(cid:241) nas one do zastanowienia si(cid:246), dlaczego piszemy konkretny
wiersz kodu i co powinien on reprezentowa(cid:232) po ewaluacji. W ten sposób
mo(cid:276)emy lepiej zrozumie(cid:232), dlaczego wiersz kodu dzia(cid:228)a tak, a nie inaczej.
112
(cid:95)
Rozdzia(cid:293) 7. Instrukcje
Kup książkęPoleć książkęSkorowidz
A
adnotacja @Lazy, 90, 92,
94
B
baza danych, 65, 139
bean, Patrz: ziarno
bezpiecze(cid:254)stwo
w(cid:241)tków, 92
C
closure, Patrz:
domkni(cid:246)cie
D
domkni(cid:246)cie, 30, 32, 33,
35, 42, 62
Don’t Repeat Yourself,
Patrz: zasada DRY
E
efekt uboczny, 41
ekspresyjno(cid:264)(cid:232), 135
ekstraktor, 118
Erjang, 100
ewaluacja
leniwa, Patrz:
ewaluacja
nierygorystyczna
nierygorystyczna,
15, 16, 87, 88, 89
rygorystyczna, 87, 88
statyczna, 89
F
first-class function,
Patrz: typ funkcyjny
funkcja, 8
anonimowa, 30
czysta, 15, 16, 41, 45,
135
ekspresyjna, 135
filter, 44
findAll, 48
getCustomerById, 45
hermetyzacja, 24, 27
jako obiekt, 21, 22
lambda, 30
lista parametrów,
22, 30
(cid:228)a(cid:254)cuchowanie
wywo(cid:228)a(cid:254), 111, 131
nazwa, 22, 30
nienazwana, 30
println, 16
przekazywanie
do funkcji, 25, 27
rekurencyjna,
Patrz: rekurencja
warto(cid:264)(cid:232) zwracana,
22, 30
wy(cid:276)szego rz(cid:246)du,
135
G
generic typing,
Patrz: typizowanie
uogólnione
Groovy, 19, 37, 48, 74,
80, 90, 92, 135
sk(cid:228)adnia, 38
guard, Patrz: stra(cid:276)nik
H
hermetyzacja, 17
statyczna, 125
Hibernate, 97
149
Kup książkęPoleć książkęniemutowalna, 65
ogon, 75, 118
pusta, 48
roz(cid:228)o(cid:276)ona, 118
M
makro, 22
mapowanie obiektowo-
-relacyjne, Patrz: ORM
maszyna wirtualna
Javy, Patrz: JVM
metoda
singletona, 137, 138
statyczna, 101, 139
ustawiaj(cid:241)ca, 66
N
niemutowalno(cid:264)(cid:232), 65, 76,
88
niewa(cid:276)no(cid:264)(cid:232), 43
nonstrict evaluation,
Patrz: ewaluacja
nierygorystyczna
notacja tablicowa, 8
Null Object, Patrz:
wzorzec projektowy
Pusty Obiekt
nullity, Patrz:
niewa(cid:276)no(cid:264)(cid:232)
O
obiekt, 125
jako kontener, 127
object-oriented
programming,
Patrz: OOP
OOP, 125, 138
operator
::, 118
sigma, 9
trójargumentowy,
79, 85, 99, 107, 110
ORM, 97
P
pattern matching,
Patrz: wzorzec
dopasowywanie
programowanie
funkcyjne, 10, 15,
100, 104, 134
imperatywne, 9
obiektowe, 10,
Patrz: OOP
przetwarzanie
równoleg(cid:228)e, 17
przypadek ko(cid:254)cowy,
73, 75, 82
pure function,
Patrz: funkcja czysta
R
rachunek lambda, 25, 30
recursion, Patrz:
rekurencja
refaktoryzacja
Groovy, 37
if-else, 22
obiekt funkcji
do wyodr(cid:246)bniania
pól, 24
rekurencja, 15, 16, 73, 74,
77, 78, 81, 137
ogonowa, 80, 136
Scala, 84
I
immutable variable,
Patrz: zmienna
niemutowalna
instrukcja, 16, 17, 99,
104, 110
blokowa, 102
ewaluacja, 8
if, 8, 114
konwersja na
dopasowywanie
do wzorca, 116,
122
match, 114
interfejs Runnable, 24
J
Java ziarno,
Patrz: ziarno
j(cid:246)zyk
Clojure,
Patrz: Clojure
Erjang, Patrz: Erjang
Groovy,
Patrz: Groovy
Scala, Patrz: Scala
JVM, 100
K
komunikat, 17, 137
konstruktor, 104
krotka, 115, 117
L
LISP, 100
lista
g(cid:228)owa, 75, 118
mapowanie, 67
150
(cid:95)
Skorowidz
Kup książkęPoleć książkęS
Scala, 19, 84, 100, 135,
139
sk(cid:228)adnia, 85
setter, Patrz: metoda
ustawiaj(cid:241)ca
side effects, Patrz:
skutki uboczne
skutki uboczne, 16, 50,
53
implementacja, 50
s(cid:228)owo kluczowe
case, 114
match, 114
this, 103
volatile, 92
statement, Patrz:
instrukcja
static evaluation, Patrz:
ewaluacja statyczna
stos, 74, 79
stra(cid:276)nik, 124
sumowanie, Patrz:
operator sigma
symbol zast(cid:246)pczy, 65
T
tail recursion, Patrz:
rekurencja ogonowa
ternary operator, Patrz:
operator
trójargumentowy
trampolina, 80
transakcja bazy danych,
65
tuple, Patrz: krotka
typ
bezpiecze(cid:254)stwo, 24
funkcyjny, 15, 16, 19,
22
zwracany, 27
typizowanie
uogólnione, 26, 27
W
warto(cid:264)(cid:232) null, 48, 77, 137
w(cid:241)tek, 137
bezpiecze(cid:254)stwo, 92
pula, 137
wiersz polece(cid:254), 129
wspó(cid:228)bie(cid:276)no(cid:264)(cid:232), 17, 100,
137
wyj(cid:241)tek, 24
wyra(cid:276)enie regularne,
113
wzorzec, 114, 121
dopasowywanie, 16,
17, 113, 118, 119,
120, 128
warunek, 124
oparty na
obiektach, 118
projektowy, 137
Opcja, 137, 138
Pusty Obiekt, 138
projektowy
Strategia, 130
prosty, 115
Z
zasada DRY, 21, 35, 36
ziarno, 110
zmienna
domkni(cid:246)ta, 34
globalna, 16
instancji, 101
leniwa, 87, 89, 90, 93
mutowalna, 60, 87,
136
niemutowalna, 15,
16, 59, 65, 66, 88,
125, 136
znak
::, 118
, 114
_, 114
= , 124
(cid:228)a(cid:254)cuch, 114
Skorowidz
(cid:95)
151
Kup książkęPoleć książkęKup książkęPoleć książkę
Pobierz darmowy fragment (pdf)