Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG
TWJ KOSZYK
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
Projektowanie
oprogramowania.
Wstp do programowania
i techniki komputerowej
Autorzy: Matthias Felleisen, Robert Bruce Findler,
Matthew Flatt, Shriram Krishnamurthi
Tumaczenie: Bartosz Grabski, Mikoaj Szczepaniak
ISBN: 83-7197-922-3
Tytu oryginau: How to Design Programs
Format: B5, stron: 644
Przykady na ftp: 32 kB
Umiejtno programowania nie ma ju charakteru czysto zawodowego. Ksigowi
musz si posugiwa arkuszami kalkulacyjnymi i edytorami tekstu, fotografowie
korzystaj z edytorw zdj, muzycy programuj syntezatory, za profesjonalni
programici tworz skomplikowane aplikacje. Programowanie jest wic bardzo
podan umiejtnoci, potrzebn nie tylko informatykom. Projektowanie
oprogramowania wymaga takich samych zdolnoci analitycznych, jak matematyka.
Jednak, w przeciwiestwie do matematyki, praca z programami jest aktywnym
sposobem zdobywania wiedzy. Obcowanie z oprogramowaniem daje moliwo staej
interakcji, co pozwala na zgbianie wiedzy, eksperymentowanie z ni oraz na sta
samoocen.
Autorzy tej klasycznej publikacji stawiaj tez, i kady powinien nauczy si, jak
projektowa oprogramowanie i wanie nauka podstaw projektowania jest jej tematem
gwnym. W ksice znajdziesz wiele podstawowych algorytmw, wyjanienia takich
poj, jak akumulacja wiedzy czy rwno ekstensjonalna i intensjonalna, sowem
wszystko to, co stanowi teoretyczn podstaw wiedzy programistycznej.
Poznasz midzy innymi:
Podstawowe struktury, z ktrych skadaj si programy komputerowe
Proste i zoony typy danych
Metody przetwarzania danych
Programowanie z uyciem rekurencji, algorytmy z nawracaniem
Projektowanie abstrakcyjne
Sposoby gromadzenia wiedzy
Wykorzystanie wektorw
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Spis treci
Przedmowa ............................................................................................................9
Dlaczego kady powinien uczy si programowa? .................................................................... 11
Metody projektowania ....................................................................................................................... 12
Wybr Scheme i DrScheme ............................................................................................................... 14
Podzia ksiki..................................................................................................................................... 15
Podzikowania .................................................................................................................................... 18
19
1.
2.
3.
4.
5.
Informacje symboliczne.............................................................................63
Proste wiczenia z symbolami.......................................................................................................... 65
6.
SPIS TRECI
7.
Rodzaje danych...........................................................................................95
Mieszanie i rozrnianie danych ..................................................................................................... 95
Projektowanie funkcji przetwarzajcych dane mieszane ........................................................... 100
Skadanie funkcji powtrka ....................................................................................................... 104
Rozszerzone wiczenie: przesuwanie figur.................................................................................. 107
Bdne dane wejciowe .................................................................................................................... 108
SPIS TRECI
197
Cz IV Projektowanie abstrakcyjne
281
SPIS TRECI
Cz V Rekursja generatywna
351
Cz VI Gromadzenie wiedzy
423
SPIS TRECI
467
539
SPIS TRECI
42. Rwno.....................................................................................................595
Rwno ekstensjonalna .................................................................................................................. 595
Rwno intensjonalna..................................................................................................................... 596
Zakoczenie ..............................................................................................629
Technika obliczeniowa..................................................................................................................... 629
Programowanie ................................................................................................................................. 630
Krok naprzd..................................................................................................................................... 631
Dodatki
633
Skorowidz..................................................................................................635
17
Przetwarzanie dwch
skomplikowanych elementw danych
236
237
wiczenia
wiczenie 17.1. W wielu wiczeniach uywalimy operacji append jzyka Scheme, ktra
pobiera trzy listy i zestawia ich elementy w jedn list:
(append (list 'a) (list 'b 'c) (list 'd 'e 'f))
;; oczekiwana warto:
(list 'a 'b 'c 'd 'e 'f)
Wykorzystaj funkcj zastap-empty-lista do zdefiniowania funkcji nasz-append, ktra
powinna dziaa identycznie jak append udostpniany w jzyku Scheme.
wiczenie 17.2. Opracuj funkcj krzyzuj, ktra pobierze list symboli oraz list liczb
i zwrci wszystkie moliwe pary symboli z liczbami.
Przykad:
(krzyzuj '(a b c) '(1 2))
;; oczekiwana warto:
(list (list 'a 1) (list 'a 2) (list 'b 1) (list 'b 2) (list 'c 1) (list 'c 2))
238
239
(else
(first dana-lista-liczb1) (first dana-lista-liczb2)
(rest dana-lista-liczb1) (rest dana-lista-liczb2) )))
Poniewa ostatnie dwa selektory dotycz list o identycznych dugociach, w oczywisty
sposb moemy je wykorzysta w naturalnej rekursji funkcji godziny->wynagrodzenia:
(define (godziny->wynagrodzenia dana-lista-liczb1 dana-lista-liczb2)
(cond
((empty? dana-lista-liczb1) )
(else
(first dana-lista-liczb1) (first dana-lista-liczb2)
(godziny->wynagrodzenia (rest dana-lista-liczb1) (rest dana-lista-liczb2)) )))
Jedynym niezwykym elementem tego szablonu jest rekursywne wywoanie funkcji zoone z dwch wyrae, z ktrych oba s selektorami dwch argumentw funkcji. Jak si
ju przekonalimy, idea dziaania funkcji jest atwa do wytumaczenia dziki zaoeniu,
e dana-lista-liczb1 i dana-lista-liczb2 maj rwn dugo.
Podczas definiowania funkcji bdziemy postpowa zgodnie z zaleceniami metody
projektowania. Pierwszy przykad oznacza, e odpowiedzi pierwszej klauzuli wyraenia cond powinna by lista pusta empty. W drugiej klauzuli mamy do dyspozycji trzy
wartoci:
(1) (first dana-lista-liczb1), ktra reprezentuje pierwszy element listy stawek godzinowych;
(2) (first dana-lista-liczb2), ktra reprezentuje pierwszy element listy przepracowanych
godzin; oraz
(3) (godziny->wynagrodzenia (rest dana-lista-liczb1) (rest dana-lista-liczb2)), ktra jest list
wynagrodze dla reszt list dana-lista-liczb1 i dana-lista-liczb2.
Aby otrzyma ostateczny wynik, musimy jedynie odpowiednio poczy te wartoci.
A dokadniej, zgodnie z opisem celu musimy obliczy wynagrodzenie dla pierwszego pracownika i skonstruowa list zoon z tej wartoci i wynagrodze pozostaych pracownikw. Oznacza to, e odpowied dla drugiej klauzuli wyraenia cond powinna wyglda nastpujco:
(cons (wynagrodzenie (first dana-lista-liczb1) (first dana-lista-liczb2))
(godziny->wynagrodzenia (rest dana-lista-liczb1) (rest dana-lista-liczb2)))
Zewntrzna funkcja wynagrodzenie pobiera dwa pierwsze elementy i oblicza odpowiednie wynagrodzenie. Listing 17.2 zawiera kompletne definicje obu funkcji.
Listing 17.2. Kompletna definicja funkcji godziny->wynagrodzenia
;; godziny->wynagrodzenia : lista-liczb lista-liczb -> lista-liczb
;; konstruuje now list zawierajc iloczyny odpowiednich
;; elementw list dana-lista-liczb1 i dana-lista-liczb2
;; ZAOENIE: listy maj rwn dugo
240
wiczenia
wiczenie 17.3. W rzeczywistym wiecie funkcja godziny->wynagrodzenia pobieraaby list struktur reprezentujcych pracownikw i list struktur reprezentujcych przebieg
prac w ostatnim miesicu. Struktura pracownika zawiera jego nazwisko, numer PESEL
oraz stawk godzinow. Struktura opisujca przebieg pracy zawiera nazwisko pracownika i liczb przepracowanych w danym miesicu godzin. Wynikiem jest lista struktur
zawierajcych nazwisko pracownika i nalene mu wynagrodzenie.
Zmodyfikuj funkcj z listingu 17.2 tak, aby pracowaa na powyszych klasach danych. Opracuj potrzebne definicje struktur i definicje danych. Zastosuj metod projektowania w procesie modyfikacji funkcji.
wiczenie 17.4. Opracuj funkcj zepnij, ktra pobierze list nazwisk oraz list numerw
telefonicznych i poczy je w list podobn do ksiki telefonicznej. Zakadajc, e mamy nastpujc definicj struktury:
(define-struct wpis (nazwisko numer))
pojedynczy wpis w ksice telefonicznej konstruujemy za pomoc instrukcji (make-wpis
s n), gdzie s jest symbolem, a n jest liczb. Za, e dane na wejciu listy maj identyczne
dugoci. Upro definicj na tyle, na ile bdzie to moliwe.
241
Powyszy program wymaga opracowania funkcji, ktra bdzie pobiera liczb naturaln
i list symboli. Obie dane wejciowe nale do klas opisanych skomplikowanymi definicjami danych, jednak inaczej ni w dwch poprzednich problemach, klasy te s cakowicie rozczne. Na listingu 17.3 przypominamy obie definicje.
Listing 17.3. Definicje danych dla funkcji wybierz-z-listy
Definicje danych:
liczba naturalna [>=1] (N[>=1]) jest albo:
(1) 1, albo
(2) (dodaj1 n), jeli n naley do N[>=1].
lista-symboli jest albo:
(1) list pust, empty, albo
(2) (cons s ls), gdzie s jest symbolem, a ls jest list symboli.
Poniewa problem jest nietypowy, powinnimy upewni si, e nasze przykady
obejmuj wszystkie wane przypadki. Ten cel osigamy zazwyczaj wybierajc po jednym przykadzie dla kadej klauzuli z definicji i wybierajc losowo elementy dla pozostaych, prostych elementw danych. W tym przykadzie taka procedura prowadzi nas
do wybrania co najmniej dwch elementw dla danej lista-symboli i dwch dla N[>= 1].
Wybierzmy empty i (cons 'a empty) dla listy, oraz 1 i 3 dla liczb naturalnych. Po dwa
przykady dla obu argumentw oznaczaj, e bdziemy mieli ich cznie cztery, nie mamy
jednak danego wprost zwizku pomidzy tymi dwoma argumentami, ani adnych ogranicze wspomnianych w kontrakcie:
(wybierz-z-listy empty 1)
;; oczekiwane zachowanie:
(error 'wybierz-z-listy "")
(wybierz-z-listy (cons 'a empty) 1)
;; oczekiwana warto:
'a
(wybierz-z-listy empty 3)
;; oczekiwane zachowanie:
(error 'wybierz-z-listy "")
(wybierz-z-listy (cons 'a empty) 3)
;; oczekiwane zachowanie:
(error 'wybierz-z-listy "")
Tylko jeden z czterech wynikw jest symbolem; w pozostaych przypadkach otrzymalimy bdy zwizane z brakiem elementw na danych listach.
242
(cons? dana-lista-symboli)
(= n 1)
(> n 1)
Wiersze tabeli opisuj dane wejciowe, dla ktrych funkcja wybierz-z-listy musi okreli,
co podano jako list symboli; w kolumnach rozrniamy dane liczby naturalne. Co wicej, w tabeli mamy cztery pola, z ktrych kade reprezentuje przypadek, w ktrym zarwno warunek odpowiedniej kolumny jak i wiersza ma warto true. Moemy to wyrazi za pomoc wyrae z operatorem and w odpowiednich komrkach tabeli:
(= n 1)
(> n 1)
(empty? dana-lista-symboli)
(and (= n 1) (empty? dana-lista-symboli))
(and (> n 1) (empty? dana-lista-symboli))
(cons? dana-lista-symboli)
(and (= n 1) (cons? dana-lista-symboli))
(and (> n 1) (cons? dana-lista-symboli))
atwo teraz wykaza, e dla dowolnej danej pary argumentw dokadnie jeden z czterech warunkw zawartych w komrkach tabeli powyej i ma warto true.
Korzystajc z naszej analizy przypadkw, moemy teraz zaprojektowa pierwsz
cz szablonu wyraenie warunkowe:
(define (wybierz-z-listy dana-lista-symboli n)
(cond
[(and (= n 1) (empty? dana-lista-symboli)) ]
[(and (> n 1) (empty? dana-lista-symboli)) ]
[(and (= n 1) (cons? dana-lista-symboli)) ]
[(and (> n 1) (cons? dana-lista-symboli)) ]))
Wyraenie cond sprawdza wszystkie cztery warunki wyrniajc wszystkie moliwoci.
Nastpnie, jeli to moliwe, musimy doda selektory do kadej klauzuli tego wyraenia:
(define (wybierz-z-listy dana-lista-symboli n)
(cond
[(and (= n 1) (empty? dana-lista-symboli))
]
[(and (> n 1) (empty? dana-lista-symboli))
(odejmij1 n) ]
[(and (= n 1) (cons? dana-lista-symboli))
(first dana-lista-symboli) (rest dana-lista-symboli)]
[(and (> n 1) (cons? dana-lista-symboli))
(odejmij1 n) (first dana-lista-symboli) (rest dana-lista-symboli) ]))
243
Dla liczby naturalnej n szablon zawiera co najwyej jeden selektor, ktry okrela poprzednika tej liczby w zbiorze liczb naturalnych. Dla listy dana-lista-symboli moliwe s
maksymalnie dwa selektory. Jednak w przypadku, w ktrym prawdziwy jest warunek
(= n 1) lub (empty? dana-lista-symboli), czyli jeden z dwch argumentw jest atomowy,
nie ma potrzeby stosowania odpowiadajcych tym danym wejciowym selektorw.
Ostatni krok w procesie konstruowania szablonu wymaga wypisania szablonu z zaznaczonymi rekursjami dla wyrae, w ktrych wyraenia selektorw zwracaj dane nalece do tej samej klasy, co dane wejciowe. W szablonie dla funkcji wybierz-z-listy takie
dziaanie ma sens jedynie dla ostatniej klauzuli cond, ktra zawiera wyraenia zarwno
dla N[>= 1], jak i dla listysymboli. Wszystkie inne klauzule zawieraj co najwyej jedno
odpowiednie wyraenie. Nie jest jednak do koca jasne, jak sformuowa w tym przypadku naturaln rekursj. Jeli zbagatelizujemy cel funkcji i w kroku konstruowania szablonu wypiszemy wszystkie moliwe rekursje, otrzymamy trzy przypadki:
(wybierz-z-listy (rest dana-lista-symboli) (odejmij1 n))
(wybierz-z-listy dana-lista-symboli (odejmij1 n))
(wybierz-z-listy (rest dana-lista-symboli) n)
Poniewa nie wiemy, ani ktra rekursja ma w tym przykadzie zastosowanie, ani czy
moemy wykorzysta wszystkie trzy rekursje, przechodzimy do nastpnego etapu.
Zgodnie z metod projektowania, przeanalizujmy kad z klauzul wyraenia cond
naszego szablonu i znajdmy prawidow odpowied na powysze pytanie:
(1) Jeli warunek (and (= n 1) (empty? dana-lista-symboli)) jest prawdziwy, funkcja wybierz-z-listy powinna wybra pierwszy element z pustej listy, co jest niemoliwe.
Odpowiedzi funkcji bdzie wic sygnalizacja o bdzie.
(2) Jeli warunek (and (> n 1) (empty? dana-lista-symboli)) jest prawdziwy, funkcja wybierz-z-listy powinna znowu wybra element z pustej listy. Odpowiedzi jest wic
znowu bd.
(3) Jeli warunek (and (= n 1) (cons? dana-lista-symboli)) jest prawdziwy, funkcja wybierz-z-listy powinna zwrci pierwszy element danej listy. Selektor (first danalista-symboli) przypomina nam, jak uzyska ten element. Wanie on bdzie odpowiedzi funkcji.
(4) W ostatniej klauzuli, jeli warunek (and (> n 1) (cons? dana-lista-symboli)) jest prawdziwy, musimy przeanalizowa, co zwracaj poszczeglne selektory:
(a) (first dana-lista-symboli) wybiera pierwszy element z listy symboli;
(b) (rest dana-lista-symboli) reprezentuje reszt listy; oraz
(c) (odejmij1 n) zwraca liczb mniejsz od jeden od danego indeksu listy.
Rozwamy przykad ilustrujcy znaczenie powyszych wyrae. Przypumy, e
funkcj wybierz-z-listy zastosowano dla listy (cons 'a (cons 'b empty)) i liczby 2:
(wybierz-z-listy (cons 'a (cons 'b empty)) 2)
Funkcja powinna zwrci symbol 'b, poniewa (first dana-lista-symboli) zwrci 'a,
natomiast (odejmij1 n) zwrci 1. Poniej przedstawiamy efekty ewentualnego zastosowania trzech naturalnych rekursji dla tych wartoci:
244
(a) (wybierz-z-listy (cons 'b empty) 1) zwraca 'b, czyli podan warto;
(b) (wybierz-z-listy (cons 'a (cons 'b empty)) 1) zwraca warto 'a, ktra jest symbolem, ale nie jest oczekiwan wartoci dla naszego problemu;
(c) (wybierz-z-listy (cons 'b empty) 2) sygnalizuje bd, poniewa indeks jest wikszy ni dugo listy.
Powysza analiza sugeruje, e powinnimy uy wyraenia (wybierz-z-listy (cons 'b
empty) 1) jako odpowiedzi ostatniej klauzuli cond. Oparte na przykadzie rozwizania s jednak czsto zawodne, powinnimy wic sprbowa zrozumie, dlaczego
to wyraenie jest odpowiednie dla naszej funkcji.
Przypomnij sobie, e zgodnie z opisem celu,
(wybierz-z-listy (rest dana-lista-symboli) (odejmij1 n))
wybiera element o indeksie (n-1) z listy (rest dana-lista-liczb). Innymi sowy, zmniejszamy
indeks o 1, skracamy list o jeden element i szukamy w nowej licie elementu o zmniejszonym indeksie. Wyraenie zwraca warto zgodn z oczekiwaniami, podobnie jak
odpowied klauzuli z warunkiem (= n 1), przy zaoeniu, e dana-lista-symboli i n s
wartociami zoonymi. Nasz wybr odpowiedzi dla ostatniej klauzuli jest wic cakowicie uzasadniony. Kompletn definicj funkcji wybierz-z-listy przedstawia listing 17.4.
Listing 17.4. Kompletna definicja funkcji wybierz-z-listy
;; wybierz-z-listy : lista-symboli N[>= 1] -> symbol
;; okrela n-ty symbol na licie dana-lista-symboli, liczc od 1;
;; sygnalizuje bd, jeli na danej licie nie ma n-tego symbolu
(define (wybierz-z-listy dana-lista-symboli n)
(cond
[(and (= n 1) (empty? dana-lista-symboli)) (error 'wybierz-z-listy "lista jest za
krtka")]
[(and (> n 1) (empty? dana-lista-symboli)) (error 'wybierz-z-listy "lista jest za
krtka")]
[(and (= n 1) (cons? dana-lista-symboli)) (first dana-lista-symboli)]
[(and (> n 1) (cons? dana-lista-symboli)) (wybierz-z-listy (rest dana-lista-symboli)
(odejmij1 n))]))
wiczenia
wiczenie 17.5. Opracuj funkcj wybierz-z-listy0, ktra wybierze elementy z listy podobnie jak wybierz-z-listy, ale poczwszy od indeksu 0.
Przykady:
(symbol=? (wybierz-z-listy0 (list 'a 'b 'c 'd) 3)
'd)
(wybierz-z-listy0 (list 'a 'b 'c 'd) 4)
;; oczekiwane zachowanie:
(error 'wybierz-z-listy0 "lista jest za krtka")
UPRASZCZANIE FUNKCJI
245
Upraszczanie funkcji
Funkcja wybierz-z-listy zaprezentowana na listingu 17.4 jest bardziej skomplikowana ni
jest to konieczne. Pierwsza i druga klauzula wyraenia cond zwracaj takie same odpowiedzi: bd. Innymi sowy, jeli wyraenie:
(and (= n 1) (empty? dana-lista-symboli))
lub
(and (> n 1) (empty? dana-lista-symboli))
ma warto true, efektem dziaania funkcji jest bd. Moemy wic wykorzysta to podobiestwo i stworzy prostsze wyraenie cond:
(define (wybierz-z-listy dana-lista-symboli n)
(cond
[(or (and (= n 1) (empty? dana-lista-symboli))
(and (> n 1) (empty? dana-lista-symboli))) (error 'wybierz-z-listy "lista jest
za krtka")]
[(and (= n 1) (cons? dana-lista-symboli)) (first dana-lista-symboli)]
[(and (> n 1) (cons? dana-lista-symboli)) (wybierz-z-listy (rest dana-lista-symboli)
(odejmij1 n))]))
Nowe wyraenie jest prostym przeoeniem naszych obserwacji na jzyk Scheme.
Aby jeszcze bardziej uproci nasz funkcj, musimy zapozna si z regu algebraiczn dotyczc wartoci logicznych:
(or (and warunek1 dany-warunek)
(and warunek2 dany-warunek))
= (and (or warunek1 warunek2)
dany-warunek)
Powysze rwnanie nazywa si prawem de Morgana. Zastosowanie go w naszej funkcji
spowoduje nastpujce uproszczenie:
(define (wybierz-z-listy n dana-lista-symboli)
(cond
[(and (or (= n 1) (> n 1))
(empty? dana-lista-symboli)) (error 'wybierz-z-listy "lista jest za krtka")]
[(and (= n 1) (cons? dana-lista-symboli)) (first dana-lista-symboli)]
[(and (> n 1) (cons? dana-lista-symboli)) (wybierz-z-listy (rest dana-lista-symboli)
(odejmij1 n))]))
Rozwa teraz pierwsz cz warunku (or (= n 1) (> n 1)). Poniewa n naley do
zbioru N[>= 1], warunek jest zawsze prawdziwy. Gdybymy jednak zastpili go sowem
true, otrzymalibymy warunek:
246
(and true
(empty? dana-lista-symboli))
ktry jest rwnowany z warunkiem (empty? dana-lista-symboli). Innymi sowy, funkcj
moemy zapisa w nastpujcy sposb:
(define (wybierz-z-listy dana-lista-symboli n)
(cond
[(empty? dana-lista-symboli) (error 'wybierz-z-listy "lista jest za krtka")]
[(and (= n 1) (cons? dana-lista-symboli)) (first dana-lista-symboli)]
[(and (> n 1) (cons? dana-lista-symboli)) (wybierz-z-listy (rest dana-lista-symboli)
(odejmij1 n))]))
Ta definicja jest znacznie prostsza ni ta, ktr zademonstrowalimy na listingu 17.4.
Moemy uproci nasz definicj jeszcze bardziej. Pierwszy warunek w ostatniej
wersji funkcji wybierz-z-listy odrzuca wszystkie przypadki, w ktrych dana-lista-symboli
jest pusta. Wyraenie (cons? dana-lista-symboli) w nastpnych dwch klauzulach bdzie
wic miao zawsze warto true. Jeli zastpimy warunek t wartoci i uprocimy wyraenie and, otrzymamy najprostsz z moliwych wersj funkcji wybierz-z-listy, ktr przedstawilimy na listingu 17.5. Mimo e ostatnia funkcja jest duo prostsza od oryginalnej,
wane jest, bymy rozumieli, e opracowalimy obie wersje w sposb systematyczny i tylko
dziki temu moemy by pewni, e dziaaj one poprawnie. Gdybymy prbowali od
pocztku tworzy wersj uproszczon, prdzej czy pniej popenilibymy bd.
Listing 17.5. Uproszczona definicja funkcji wybierz-z-listy
;; wybierz-z-listy : lista-symboli N[>= 1] -> symbol
;; okrela n-ty symbol na licie dana-lista-symboli, liczc od 1;
;; sygnalizuje bd, jeli na danej licie nie ma n-tego symbolu
(define (wybierz-z-listy dana-lista-symboli n)
(cond
[(empty? dana-lista-symboli) (error 'wybierz-z-listy "lista jest za krtka")]
[(= n 1) (first dana-lista-symboli)]
[(> n 1) (wybierz-z-listy (rest dana-lista-symboli) (odejmij1 n))]))
wiczenia
wiczenie 17.6. Opracuj funkcj zastap-empty-lista zgodnie ze strategi z podrozdziau
Jednoczesne przetwarzanie dwch list. Przypadek 2.. Nastpnie systematycznie upraszczaj definicj funkcji.
wiczenie 17.7. Upro definicj funkcji wybierz-z-listy0 z wiczenia 17.5 lub wyjanij,
dlaczego nie mona jej uproci.
247
(cons? dana-lista-symboli)
(= n 1)
(> n 1)
W kolumnach wyliczamy warunki rozrniajce podklasy pierwszego parametru, w wierszach wyliczamy warunki dla drugiego parametru.
Tabela pomaga w opracowaniu zarwno zbioru przykadw dla naszej funkcji, jak
i jej szablonu. Jeli chodzi o przykady, musz one pokrywa wszystkie moliwe przypadki. Oznacza to, e musimy opracowa przynajmniej po jednym przykadzie dla kadej komrki tabeli.
248
Jeli chodzi o szablon, musimy w nim zawrze po jednej klauzuli wyraenia cond
dla kadej komrki. Kada z tych klauzul musi zawiera wszystkie moliwe selektory
dla obu parametrw. Jeli jeden z nich jest atomowy, nie ma oczywicie potrzeby stosowania selektorw. Wreszcie, zamiast pojedynczej naturalnej rekursji moe zaistnie konieczno wprowadzenia wielu rekursji. Dla funkcji wybierz-z-listy znalelimy trzy
przypadki. Generalnie wszystkie moliwe kombinacje selektorw s potencjalnymi kandydatami do wykorzystania w naturalnej rekursji. Poniewa nie moemy wiedzie, ktre
z nich s konieczne, a ktre nie s, wypisujemy je wszystkie i wybieramy te, ktre bd
odpowiednie dla naszej definicji funkcji.
Podsumowujc, projektowanie funkcji dla wielu parametrw opiera si na pewnej
odmianie starej metody projektowania. Kluczowe znaczenie ma przeoenie definicji danych na tabel demonstrujc wszystkie moliwe i interesujce nas kombinacje. Tworzenie przykadw dla funkcji i jej szablonu musi opiera si w jak najwikszym stopniu
wanie na tej tabeli. Wypenienie pustych przestrzeni w szablonie wymaga, jak zreszt
wszystkie pozostae czynnoci, praktyki.
wiczenia z przetwarzania
dwch zoonych danych wejciowych
wiczenia
wiczenie 17.8. Opracuj funkcj scalaj, ktra pobierze dwie listy liczb posortowane rosnco. Wynikiem funkcji bdzie pojedyncza, posortowana lista liczb zawierajca wszystkie liczby z obu list (i adnej innej). Poszczeglne liczby powinny wystpowa w licie
wyjciowej tyle razy, ile razy pojawiy si w dwch listach wejciowych.
Przykady:
(scalaj (list 1 3 5 7 9) (list 0 2 4 6 8))
;; oczekiwana warto:
(list 0 1 2 3 4 5 6 7 8 9)
(scalaj (list 1 8 8 11 12) (list 2 3 4 8 13 14))
;; oczekiwana warto:
(list 1 2 3 4 8 8 8 11 12 13 14)
wiczenie 17.9. Celem tego wiczenia jest opracowanie nowej wersji gry w szubienic
z rozdziau 6. dla sw o dowolnej dugoci.
Stwrz definicj danych reprezentujcych sowa dowolnej dugoci za pomoc list.
Litera powinna by reprezentowana przez symbole od 'a do 'z oraz symbol '_.
Opracuj funkcj odslon-liste, ktra pobierze trzy argumenty:
(1) Wybrane sowo, ktre naley odgadn.
(2) Sowo statusu, ktre okrela, jak du cz sowa odgadlimy do te pory.
(3) Liter, ktra reprezentuje nasz aktualn prb.
249
Funkcja zwrci nowe sowo statusu, ktre skada si z dowolnych liter i znakw '_. Pola
w nowym sowie statusu okrelamy porwnujc prb z kad par liter ze sowa statusu
i wybranego sowa:
(1) Jeli prba jest rwna danej literze w wybranym sowie, wstawiamy t liter w odpowiednie miejsce w sowie statusu.
(2) W przeciwnym przypadku nowa litera odpowiada literze ze sowa statusu.
Przetestuj funkcj odslon-liste dla nastpujcych przykadw:
(odslon-liste (list 't 'e 'a) (list '_ 'e '_) 'u)
(odslon-liste (list 'a 'l 'e) (list 'a '_ '_) 'e)
(odslon-liste (list 'a 'l 'l) (list '_ '_ '_) 'l)
Okrel najpierw rcznie jakie powinny by rezultaty powyszych wywoa.
Zastosuj pakiet szkoleniowy hangman.ss i funkcje dorysuj-nastepna-czesc (patrz wiczenie 6.27) oraz odslon-liste, aby rozegra gr w szubienic. Oblicz take warto nastpujcego wyraenia:
(hangman-list odslon-liste dorysuj-nastepna-czesc)
Funkcja hangman-list (udostpniona we wspomnianym pakiecie nauczania) wybiera losowe sowo i wywietla okno z menu wyboru liter. Wybierz litery i gdy bdziesz gotowy,
kliknij przycisk Check, aby sprawdzi, czy Twj strza by trafny. Powodzenia!
wiczenie 17.10. Robotnicy w fabryce podbijaj swoje karty czasowe rano, gdy przychodz do pracy, i po poudniu gdy wychodz. Obecnie stosuje si elektroniczne karty
zawierajce numer pracownika i liczb przepracowanych godzin. Ponadto akta pracownika zawieraj zawsze jego nazwisko, numer i stawk godzinow.
Opracuj funkcj godziny->wynagrodzenia2, ktra pobierze list akt pracowniczych oraz
list (elektronicznych) kart. Funkcja bdzie oblicza miesiczne wynagrodzenie kadego
pracownika odpowiednio dopasowujc, na podstawie numeru pracownika, dane z jego
akt z danymi zapisanymi na karcie. Jeli zabraknie danej pary lub numery pracownikw
nie bd si zgadza, funkcja zatrzyma si z odpowiednim komunikatem o bdzie. Za,
e istnieje co najwyej jedna karta dla jednego pracownika i dla odpowiadajcemu mu
numeru.
Wskazwka: Ksigowy posortowaby najpierw obie listy wedug numerw pracownikw.
wiczenie 17.11. Kombinacja liniowa jest sum kilku skadnikw liniowych, co oznacza,
e zwraca zmienne i liczby. Te drugie nazywamy w tym kontekcie wspczynnikami.
Oto kilka przykadw:
5x
5 x + 17 y
5 x + 17 y + 3 z
We wszystkich trzech przykadach wspczynnikiem przy x jest 5, przy y jest liczba 17,
a jedynym przy z jest 3.
250
Jeli mamy dane wartoci zmiennych, moemy okreli warto wielomianu. Np. jeli x = 10, wartoci iloczynu 5 x bdzie 50; jeli x = 10 i y = 1, wielomian 5 x + 17 y
przyjmie warto 67; jeli za x = 10, y = 1 i z = 2, wyraenie 5 x + 17 y + 3 z bdzie
miao warto 73.
W przeszoci opracowalibymy funkcje obliczajce wartoci liniowych kombinacji
dla poszczeglnych wartoci. Alternatywn reprezentacj takich wielomianw jest lista
wspczynnikw. Powysze kombinacje byyby reprezentowane przez takie listy:
(list 5)
(list 5 17)
(list 5 17 3)
Powysza reprezentacja zakada, e zgadzamy si zawsze uywa zmiennych w okrelonej kolejnoci.
Opracuj funkcj wartosc, ktra pobierze reprezentacj wielomianu (lista wspczynnikw) oraz list liczb (wartoci zmiennych). Obie listy maj t sam dugo. Funkcja
powinna zwrci warto tego wielomianu dla danych wartoci zmiennych.
wiczenie 17.12. Ewa, Joanna, Dorota i Maria s siostrami, ktre chciayby zaoszczdzi
pienidze i ograniczy wysiek zwizany z kupowaniem prezentw gwiazdkowych. Postanowiy wic przeprowadzi losowanie, ktre przypisze kadej z nich jedn osob, ktra
nastpnie zostanie obdarowana prezentem. Poniewa Joanna jest programist komputerowym, siostry poprosiy j o napisanie programu, ktry przeprowadzi losowanie w sposb bezstronny. Program nie moe oczywicie przypisa adnej siostrze jej samej jako
odbiorcy prezentu.
Oto definicja funkcji wybierz-prezent, ktra pobiera list rnych imion (symboli) i wybiera losowo jeden z ukadw listy, w ktrym aden z elementw nie pozosta na swoim
miejscu:
;; wybierz-prezent: lista-imion -> lista-imion
;; wybiera "losowy", inny ni oryginalny ukad imion
(define (wybierz-prezent imiona)
(wybierz-losowo
(nie-takie-same imiona (uklady imiona))))
Przypomnij sobie podobn funkcj z wiczenia 12.6, ktra pobieraa list symboli i zwracaa list wszystkich moliwych ukadw zoonych z elementw tej listy.
Opracuj zewntrzne funkcje:
(1) wybierz-losowo : lista-list-imion -> lista-imion, ktra pobierze list elementw i losowo wybierze jeden z nich;
(2) nie-takie-same : lista-imion lista-list-imion -> lista-list-imion, ktra pobierze list imion
L oraz list ukadw i zwrci list takich ukadw, w ktrych adne imi nie wystpuje na takiej samej pozycji co w danej licie imion.
Dwie permutacje s ze sob zgodne na pewnej pozycji, jeli moemy pobra to
samo imi z obu list za pomoc sowa first lub takiej samej liczby sw rest. Np. listy (list 'a 'b 'c) oraz (list 'c 'a 'b) nie s ze sob zgodne; natomiast listy (list 'a 'b 'c)
i (list 'c 'b 'a) zgadzaj si ze sob na drugiej pozycji. Moemy to udowodni stosujc dla obu list operacj rest poprzedzajc first.
251
Rozszerzone wiczenie:
obliczanie wyrae jzyka Scheme. Cz 2.
Celem tego podrozdziau jest rozszerzenie moliwoci programu stworzonego w rozdziale 14. (podrozdzia Rozszerzone wiczenie: obliczanie wyrae jzyka Scheme) tak,
by mg radzi sobie z wywoaniami i definicjami funkcji. Innymi sowy, nowy program
powinien symulowa, co staoby si w rodowisku DrScheme, gdybymy wpisali dane
wyraenie w oknie Interactions i kliknli przycisk Execute. Aby uproci sobie zadanie,
zakadamy, e wszystkie funkcje zdefiniowane w oknie Definitions pobieraj po jednym
argumencie.
252
wiczenia
wiczenie 17.14. Rozszerz definicj danych z wiczenia 14.17 tak, aby byo moliwe reprezentowanie wywoa funkcji w wyraeniach. Wywoanie powinno by reprezentowane jako struktura z dwoma polami. Pierwsze pole zawiera nazw funkcji, drugie
jedn reprezentacj wyraenia bdcego argumentem funkcji.
Kompletny program obliczajcy wartoci wyrae powinien obsugiwa take definicje funkcji.
wiczenie 17.15. Opracuj definicj struktury i definicj danych dla definicji funkcji.
Przypomnij sobie, e definicja funkcji zawiera trzy istotne atrybuty:
(1) Nazw funkcji.
(2) Nazw parametru.
(3) Ciao funkcji.
Taka charakterystyka funkcji sugeruje wprowadzenie struktury z trzema polami. Pierwsze dwa zawieraj symbole, ostatni reprezentuje ciao funkcji, ktre jest wyraeniem.
Prze nastpujce definicje na wartoci w jzyku Scheme:
(define (f x) (+ 3 x))
(define (g x) ( 3 x))
(define (h u) (f ( 2 u)))
(define (i v) (+ ( v v) ( v v)))
(define (k w) ( (h w) (i w)))
Opracuj wicej przykadw i podobnie prze je na nasz reprezentacj.
wiczenie 17.16. Opracuj funkcj interpretuj-z-jedna-definicja. Funkcja pobierze reprezentacj wyraenia Scheme i reprezentacj definicji funkcji P.
Pozostae wyraenia z wiczenia 14.17 powinny by interpretowane jak wczeniej.
W przypadku pojawienia si reprezentacji zmiennej w wyraeniu funkcja interpretuj-zjedna-definicja powinna zasygnalizowa bd. Dla wywoania funkcji P w wyraeniu funkcja interpretuj-z-jedna-definicja powinna:
(1) obliczy warto argumentu;
(2) zastpi wszystkie wystpienia parametru funkcji P w jej ciele wartoci obliczonego w poprzednim kroku argumentu; oraz
(3) obliczy nowe wyraenie za pomoc rekursji. Oto szkic takiego dziaania:1
(oblicz-z-jedna-definicja (zastp )
dana-definicja-funkcji)
Dla wszystkich innych wywoa funkcja interpretuj-z-jedna-definicja powinna sygnalizowa bd.
1
253
RWNO I TESTOWANIE
Rwno i testowanie
Wiele opracowanych przez nas funkcji zwraca listy. Kiedy je testujemy, musimy porwnywa dwie listy: wyniki zwracane przez funkcje z przewidywanymi wartociami. Porwnywanie list rcznie jest nudne i nie daje pewnoci, e nie popenilimy bdu.
Opracujmy funkcj, ktra pobiera dwie listy liczb i okrela, czy s sobie rwne:
;; lista=? : lista-liczb lista-liczb -> boolean
;; okrela, czy dana-lista i inna-lista
;; zawieraj te same liczby w tym samym porzdku
(define (lista=? dana-lista inna-lista) )
Opis celu ulepsza oglne przeznaczenie funkcji i przypomina nam, e o ile klienci mog
uwaa dwie listy za rwne, jeli zawieraj te same elementy, niezalenie od kolejnoci,
to programici s bardziej dokadni i wczaj kolejno elementw jako element porwnania. Kontrakt i opis celu pokazuj take, e lista=? jest funkcj przetwarzajc dwie
skomplikowane wartoci i w rzeczywistoci to nas najbardziej interesuje.
Porwnywanie dwch list oznacza, e musimy przejrze wszystkie elementy obu list.
Eliminuje to moliwo projektowania funkcji lista=? linia po linii, jak w przypadku funkcji
zastap-empty-lista z podrozdziau Jednoczesne przetwarzanie dwch list. Przypadek 1..
Na pierwszy rzut oka nie istnieje aden zwizek pomidzy dwiema listami wejciowymi,
co sugeruje, e powinnimy wykorzysta zmodyfikowan metod projektowania.
Zacznijmy wic od tabeli:
(empty? dana-lista)
(empty? inna-lista)
(cons? inna-lista)
(cons? dana-lista)
254
RWNO I TESTOWANIE
255
256
Mimo e wykonany przez nas krok wyglda na prosty, jego efektem jest niepoprawna
definicja. Celem wypisania warunkw w wyraeniu cond jest upewnienie si, e wszystkie selektory s poprawne.
aden element w specyfikacji funkcji lista=? nie sugeruje jednak, e inna-lista jest
skonstruowana za pomoc instrukcji cons, jeli dana-lista jest skonstruowana za pomoc
tej samej instrukcji.
Moemy poradzi sobie z tym problemem za pomoc dodatkowego warunku:
(define (lista=? dana-lista inna-lista)
(cond
[(empty? dana-lista) (empty? inna-lista)]
[(cons? dana-lista)
(and (cons? inna-lista)
(and (= (first dana-lista) (first inna-lista))
(lista=? (rest dana-lista) (rest inna-lista))))]))
Dodatkowym warunkiem jest (cons? inna-lista), co oznacza, e lista=? zwrci false, jeli
warunek (cons? dana-lista) bdzie prawdziwy oraz (cons? inna-lista) bdzie faszywy. Jak
pokazuj przykady, taki wanie powinien by podany efekt.
Podsumowujc, funkcja lista=? pokazuje, e czasami moemy zastosowa wicej ni
jedn metod projektowania w celu opracowania danej funkcji. Efekty prac s rne,
mimo e s ze sob blisko powizane; faktycznie, moglibymy udowodni, e obie funkcje zwracaj zawsze takie same wyniki dla tych samych danych wejciowych. Podczas
drugiego procesu tworzenia funkcji wykorzystalimy take spostrzeenia z pierwszej
prby.
wiczenia
wiczenia 17.18. Przetestuj obie wersje funkcji lista=?.
wiczenie 17.19. Upro pierwsz wersj funkcji lista=?. Oznacza to, e powiniene poczy ssiadujce klauzule wyraenia cond, ktre zwracaj takie same wyniki, czc
ich warunki za pomoc operatora or. Jeli to konieczne, poprzestawiaj klauzule i uyj
sowa else w ostatniej klauzuli w ostatecznej wersji funkcji.
wiczenie 17.20. Opracuj funkcj sym-lista=?. Funkcja okreli, czy dwie listy symboli s
sobie rwne.
wiczenie 17.21. Opracuj funkcj zawiera-te-same-liczby, ktra okreli, czy dwie listy liczb
zawieraj te same liczby, niezalenie od ich kolejnoci. Np. wyraenie:
(zawiera-te-same-liczby (list 1 2 3) (list 3 2 1))
zwrci warto true.
wiczenie 17.22. Klasy liczb, symboli i wartoci logicznych nazywamy czasami atomami:2
2
RWNO I TESTOWANIE
257
258
W drugiej klauzuli wyraenia cond ponownie musimy postpowa podobnie jak w przypadku funkcji godziny->wynagrodzenia i lista=?. Oznacza to, e mwimy, i inna-strona
musi mie taki sam ksztat co dana-strona, jeli ma by identyczna i mamy przetwarza obie
strony w analogiczny sposb. Rozumowanie dla drugiej klauzuli jest podobne.
W miar ulepszania powyszego szablonu musimy znowu doda warunki zwizane
z parametrem inna-strona, aby upewni si, e odpowiednie selektory bd dziaa poprawnie:
(define (www=? dana-strona inna-strona)
(cond
[(empty? dana-strona) (empty? inna-strona)]
[(symbol? (first dana-strona))
(and (and (cons? inna-strona) (symbol? (first inna-strona)))
(and (symbol=? (first dana-strona) (first inna-strona))
(www=? (rest dana-strona) (rest inna-strona))))]
[else
(and (and (cons? inna-strona) (list? (first inna-strona)))
(and (www=? (first dana-strona) (first inna-strona))
(www=? (rest dana-strona) (rest inna-strona))))]))
Musimy zwaszcza upewni si w drugiej i trzeciej klauzuli, e inna-strona jest skonstruowan list, oraz e jej pierwszy element jest symbolem lub list. W przeciwnym przypadku funkcja byaby analogiczna z funkcj lista=? i dziaaaby w identyczny sposb.
wiczenia
wiczenie 17.23. Narysuj tabel opart na definicji danych dla prostej strony WWW.
Opracuj przynajmniej po jednym przykadzie dla kadego z dziewiciu przypadkw.
Przetestuj funkcj www=? dla tych przykadw.
wiczenie 17.24. Opracuj funkcj posn=?, ktra pobiera dwie struktury posn i okrela,
czy s identyczne.
wiczenie 17.25. Opracuj funkcj drzewo=?, ktra pobierze dwa drzewa binarne i okreli,
czy s identyczne.
wiczenie 17.26. Przeanalizuj ponisze dwie wzajemnie rekursywne definicje danych:
s-lista jest albo:
(1) list pust, empty, albo
(2) (cons s sl), gdzie s jest s-wyr, za sl jest list s-lista.
s-wyr jest albo:
(1)
(2)
(3)
(4)
liczb,
wartoci logiczn,
symbolem,
list s-lista.
RWNO I TESTOWANIE
259
Opracuj funkcj s-lista=?, ktra pobiera dwie listy s-lista i okrela, czy s identyczne. Podobnie jak w przypadku list liczb, dwie listy zgodne z definicj klasy s-lista s sobie
rwne, jeli zawieraj te same elementy na analogicznych pozycjach.
Skoro przeanalizowalimy ju problem rwnoci pewnych wartoci, moemy wrci do oryginalnego rda rozwaa w tym rozdziale: funkcji testujcych. Przypumy,
e chcemy przetestowa funkcj godziny->wynagrodzenia z podrozdziau Jednoczesne
przetwarzanie dwch list. Przypadek 2.:
(godziny->wynagrodzenia (cons 5.65 (cons 8.75 empty))
(cons 40 (cons 30 empty)))
= (cons 226.0 (cons 262.5 empty))
Jeli po prostu wpiszemy wywoanie tej funkcji w oknie Interactions lub dodamy je na
kocu okna Definitions, musimy rcznie porwna otrzymany wynik z przewidywan
wartoci. Dla krtkich list, podobnych do powyszej, jest to moliwe; dla dugich list,
gbokich stron WWW lub innych duych danych zoonych rczne porwnywanie moe by rdem bdw.
Korzystajc z funkcji porwnujcych podobnych do lista=?, moemy znacznie zredukowa konieczno rcznego porwnywania wynikw testw. W naszym obecnym
przypadku moemy doda nastpujce wyraenie:
(lista=?
(godziny->wynagrodzenia (cons 5.65 (cons 8.75 empty))
(cons 40 (cons 30 empty)))
(cons 226.0 (cons 262.5 empty)))
na kocu okna Definitions. Jeli klikniemy teraz przycisk Execute, musimy tylko odczyta
w oknie Interactions, czy wszystkie podobne testy zwrciy warto true.
W rzeczywistoci moemy i jeszcze dalej. Moemy napisa funkcj testujc podobn do tej z listingu 17.6. Klasa wynik-testow skada si z wartoci i listy czterech elementw: acucha "ze wyniki testw:" i trzech list. Korzystajc z naszej nowej zewntrznej funkcji, moemy przetestowa godziny->wynagrodzenia w sposb nastpujcy:
(testuj-godziny->wynagrodzenia
(cons 5.65 (cons 8.75 empty))
(cons 40 (cons 30 empty))
(cons 226.0 (cons 262.5 empty)))
Listing 17.6. Funkcja testujca
;; testuj-godziny->wynagrodzenia : lista-liczb lista-liczb lista-liczb -> wynik-testow
;; testuje funkcj godziny->wynagrodzenia
(define (testuj-godziny->wynagrodzenia dana-lista inna-lista oczekiwany-wynik)
(cond
[(lista=? (godziny->wynagrodzenia dana-lista inna-lista) oczekiwany-wynik)
true]
[else
(list "ze wyniki testw:" dana-lista inna-lista oczekiwany-wynik)]))
260
wiczenia
wiczenie 17.27. Zdefiniuj, za pomoc funkcji equal?, funkcj testujc funkcj zastapempty-lista z podrozdziau Jednoczesne przetwarzanie dwch list. Przypadek 1.. Sformuuj take przykady bdce przypadkami testowymi w tej funkcji.
wiczenie 17.28. Zdefiniuj funkcj testuj-wybierz-z-listy, ktra bdzie zarzdza przypadkami testowymi dla funkcji wybierz-z-listy z podrozdziau Jednoczesne przetwarzanie dwch list. Przypadek 1.. Sformuuj przykady z rozdziau jako przypadki testowe
dla funkcji testuj-wybierz-z-listy.
wiczenie 17.29. Zdefiniuj funkcj testuj-interpretuj, ktra bdzie przeprowadza testy
funkcji interpretuj-z-definicjami za pomoc funkcji equal?. Sformuuj ponownie przypadki
testowe za pomoc nowej funkcji.