Jaka funkcja, ka??dy widzi
kwiecień 19th, 2006 at 11:20 pm (scheme)
Scheme nie bez przyczyny jest nazywany jÄ?zykiem funkcyjnym. Funkcje sÄ? podstawowym budulcem ka??dego programu, a ich interpretacja i wykonanie nadajÄ? kszta??t procesom. W zwiÄ?zku z tym funkcje posiadajÄ? w Scheme odpowiednie przywileje. Wspomniane sÄ? one zar??wno w wyk??adzie 2a, jak i pod koniec pierwszego rozdzia??u Wizard Booka. Oto one:
-
Funkcje mogÄ? byÄ? nazwane
Wspomnia??em ju?? o dw??ch sposobach nazywania procedur. Jeden z nich to oczywi??cie:
(define (<nazwa funkcji> <argumenty>) <zwracana wartosc>)
Drugi spos??b bazuje na formie specjalnej
lambdazwracajÄ?cej anonimowÄ? procedurÄ?.(define <nazwa funkcji> (lambda (<argumenty>) <zwracana wartosc>))
-
Funkcje mogÄ? byÄ? argumentami innych funkcji
Z tej w??a??ciwo??ci skorzysta??em przy definiowaniu funkcji
list-op. Przyjmowa??a ona jako jedyny sw??j argument operator taki jak + lub -. -
Funkcje mogÄ? byÄ? zwracane przez inne funkcje
I tutaj ponownie muszÄ? wspomnieÄ? funkcjÄ?
list-op. Zwraca??a ona bowiem funkcjÄ? implementujÄ?cÄ? pisemne dzia??anie na ciÄ?gach cyfr. Ciekawe zastosowanie tej zasady poznaÄ? mo??na w Ä?wiczeniu 1.4:(define (a-plus-abs-b a b) ((if (> b 0) + -) a b))
Operatorem kombinacji
((if (> b 0) + -) a b)jest kombinacja(if (> b 0) + -). StosujÄ?c dobrze nam znany model oblicze?? mo??na z ??atwo??ciÄ? przewidzieÄ? zachowanie funkcjia-plus-abs-b. Mianowicie operatorem stanie siÄ?+je??elibbÄ?dzie wiÄ?ksze od zera. W przeciwnym wypadku operatorem kombinacji bÄ?dzie-. Funkcja jest wiÄ?c odpowiednikiem matematycznego zapisu:a + |b|(gdzie pionowe kreski reprezentujÄ? warto??Ä? bezwzglÄ?dnÄ?). -
Funkcje mogÄ? byÄ? elementami struktur danych
Z tego jeszcze nie korzysta??em, ale z pewno??ciÄ? w drugim rozdziale znajdzie siÄ? wiele okazji do zastosowania tej w??a??ciwo??ci.
Ju?? na samym poczÄ?tku autorzy Wizard Booka przedstawiajÄ? dwa modele oblicze?? warto??ci funkcji: normalnÄ? i stosowanÄ? kolejno??Ä? obliczania. WykorzystujÄ?c kolejno??Ä? normalnÄ? najpierw rozwijamy ca??e wyra??enie do momentu a?? bÄ?dzie zawieraÄ? tylko warto??ci pierwotne (jak liczby i funkcje wbudowane). Kolejno??Ä? stosowana, bli??sza sposobowi, w jaki dzia??ajÄ? interpretery, sukcesywnie rozwija i oblicza warto??ci kolejnych czynnik??w kombinacji. Chocia?? wszystkie omawiane przez nas do tej pory programy dzia??ajÄ? dok??adnie tak samo dla obu modeli oblicze??, w Ä?wiczeniu 1.5 autorzy podajÄ? ciekawy przyk??ad procedury zachowujÄ?cej siÄ? odmiennie przy u??yciu normalnej i stosowanej kolejno??ci obliczania. Kod wyglÄ?da tak:
(define (p) (p))
(define (test x y)
(if (= x 0)
0
y))
(test 0 (p))
W przypadku normalnej kolejno??ci obliczania rozwiniÄ?cie ostatniej linijki wyglÄ?da nastÄ?pujÄ?co:
(if (= 0 0)
0
(p))
Wywo??anie funkcji test jest zastÄ?powane przez formÄ? specjalnÄ? if. To wyra??enie za??, w zwiÄ?zku ze spe??nionym warunkiem, upraszczane jest do warto??ci nastÄ?pnika, czyli do zera. Ca??e wyra??enie zwr??ci wiÄ?c warto??Ä? zero. Inaczej jednak zachowa siÄ? interpreter implementujÄ?cy stosowanÄ? kolejno??Ä? obliczania. W tym przypadku zasady obliczania ka??Ä? rozwinÄ?Ä? wszystkie elementy kombinacji jeszcze zanim wywo??amy procedurÄ?. Pr??ba rozwiniÄ?cia (p) spowoduje jednak niesko??czonÄ? pÄ?tlÄ? i w efekcie program nigdy siÄ? nie zako??czy. I tak w??a??nie stanie siÄ? je??eli spr??bujemy uruchomiÄ? powy??szy kod w interpreterze DrScheme.
Jak widaÄ? z ostatniego przyk??adu, by m??c pisaÄ? (i czytaÄ?) kod w jÄ?zyku Scheme trzeba znaÄ? nie tylko metodÄ? interpretacji kombinacji, ale i specyficznÄ? sk??adniÄ? form specjalnych. Formy specjalne, chocia?? wyglÄ?dajÄ? jak funkcje, majÄ? w??asne zasady obliczania wyra??e??. Najlepiej nam znanÄ? formÄ? specjalnÄ? jest define. Gdy piszemy (define x 42) nie jest obliczana warto??Ä? zmiennej x, co mia??o by miejsce, gdyby define by??o zwyk??Ä? funkcjÄ?. Bardziej wyrafinowany przyk??ad znale??Ä? mo??na w Ä?wiczeniu 1.6, kt??rego tre??Ä? pozwolÄ? sobie w ca??o??ci przytoczyÄ?.
Liz P. Haker nie rozumie, dlaczego
ifmusi byÄ? formÄ? specjalnÄ?: “Dlaczego nie mogÄ? zdefiniowaÄ? jej jako zwyk??ej procedury za pomocÄ?cond?”. Przyjaci????ka Liz, Ewa Lu Ator, twierdzi, ??e rzeczywi??cie mo??na tak zrobiÄ?, i definiuje nowÄ? wersjÄ?if:(define (new-if predykat nastepnik alternatywa) (cond (predykat nastepnik) (else alternatywa)))Ewa pokazuje Liz program:
(new-if (= 2 3) 0 5) 5 (new-if (= 1 1) 0 5) 0Zachwycona Liz stosuje
new-ifw programie obliczajÄ?cym pierwiastki kwadratowe:(define (sqrt-iter guess x) (new-if (good-enough? guess x) guess (sqrt-iter (improve guess x) x)))Co siÄ? stanie, gdy Liz spr??buje u??yÄ? tego programu do obliczenia pierwiastka kwadratowego? Odpowied?? uzasadnij.
Wystarczy wykonaÄ? jeden cykl podstawienia, by zauwa??yÄ? jak dzia??aÄ? bÄ?dzie ca??a procedura. Interpreter napotykajÄ?c na kombinacjÄ? zawierajÄ?cÄ? new-if zastosuje zwyk??e zasady obliczania, bo new-if nie jest formÄ? specjalnÄ?, ale procedurÄ?. Przed wywo??aniem obliczy wiÄ?c kolejne argumenty procedury: kombinacjÄ? (good-enough? guess x), warto??Ä? guess i kombinacjÄ? (sqrt-iter (improve guess x) x). Obliczenie tej ostatniej wymagaÄ? bÄ?dzie oczywi??cie wywo??anie funkcji sqrt-iter. A obliczenie jej jest r??wnowa??ne z obliczeniem warto??ci kombinacji z operatorem new-if. To wiÄ?c doprowadzi do rozwiniÄ?cia wspomnianych ju?? trzech argument??w. I tak w k????ko, program wpadnie w niesko??czonÄ? pÄ?tlÄ?.
Skoro ju?? jeste??my przy Ä?wiczeniach, przedstawiÄ? jeszcze swoje rozwiÄ?zania Ä?wicze?? 1.7 i 1.8. Ä?wiczenie 1.7 polega na ulepszeniu funkcji obliczajÄ?cej pierwiastek kwadratowy w taki spos??b, by by??a bardziej dok??adna dla ma??ych warto??ci argumentu.
(define (sqrt-iter guess lastguess x)
(if (good-enough? guess lastguess)
guess
(sqrt-iter (improve guess x) guess
x)))
(define (good-enough? guess lastguess)
(< (abs (- guess lastguess)) 0.0001))
(define (sqrt x)
(sqrt-iter 1.0 0.0 x))
; pozostale funkcje pozostaja niezmienione
W Ä?wiczeniu 1.8 nale??y zaimplementowaÄ? metodÄ? Newtona s??u??Ä?cÄ? do obliczania pierwiastk??w sze??ciennych. RozwiÄ?zanie:
(define (sqrt x)
(define (sqrt-iter guess lastguess)
(if (good-enough? guess lastguess)
guess
(sqrt-iter (improve guess) guess)))
(define (improve guess)
(/ (+ (/ x (* guess guess)) (* 2 guess)) 3))
(define (good-enough? guess lastguess)
(< (abs (- guess lastguess)) 0.0001))
(sqrt-iter 1.0 0.0))
Do funkcji powr??cimy jeszcze wielokrotnie, na dzisiaj jednak to ju?? wszystko.