wykład 3
Transkrypt
wykład 3
Definicje wyższego poziomu • Interpreter Scheme-a nie będzie „narzekad” w przypadku wystąpienia niezdefionowanej zmiennej w ciele wyrażenia lambda dopóki nie będzie zastosowana • Przykład braku informacji o błędzie (define proc1 (lambda (x y) (proc2 y x)) LISP 1 Definicje wyższego poziomu • Próba zastosowania proc1 przed definicją proc2 spowoduje wystąpienie błędu • Przykładowe utworzenie proc2 i wywołanie funkcji proc1 (define proc2 cons) (proc1 'a 'b) LISP (b . a) 2 Definicje wyższego poziomu • Scheme akceptuje założenie o zdefiniowaniu proc2 w innym miejscu kodu programu • Nie uruchamianie proc1 przed zdefiniowaniem proc2 nie generuje błędu • Definicje funkcji mogą byd umieszczane w dowolnej kolejności • Użyteczny mechanizm do bardziej czytelnego pisania programów i organizacji w pliku definicji funkcji LISP 3 Wyrażenia warunkowe • Wyrażenie if ma postad: (if test consequent alternative) gdzie: – consequent – wyznaczane wyrażenie jeżeli test jest prawdą – alternative – oceniane wyrażenie jeżeli test jest fałszem LISP 4 Wyrażenia warunkowe • Rozważmy funkcje modulus • Jeżeli argument n jest ujemny, modulus zwraca –n, w przeciwnym przypadku zwracane jest n (define modulus (lambda (n) (if (< n 0) (- 0 n) n))) (modulus 77) (modulus -77) 77 77 LISP 5 Wyrażenia warunkowe • Inne sposoby definicji funkcji modulus (define modulus (lambda (n) (if (>= n 0) n (- 0 n)))) (define modulus (lambda (n) (if (not (< n 0)) n (- 0 n)))) LISP 6 Wyrażenia warunkowe (define modulus (lambda (n) (if (or (> n 0) (= n 0)) n (- 0 n)))) (define modulus (lambda (n) (if (= n 0) 0 (if (< n 0)(- 0 n) n)))) (define modulus (lambda (n) ((if (>= n 0) + -)0 n))) LISP 7 Wyrażenia warunkowe • if jest formą składniową – nie procedurą • Rozważmy funkcję reciprocal (define reciprocal (lambda (n) (if (= n 0) "oops!" (/ 1 n)))) LISP 8 Wyrażenia warunkowe • Forma składniowa or działa podobnie jak if • Ogólna postad wyrażenia or (or exp ...) • Wartośd wyrażenia (or)jest fałszem • Każde exp jest oceniane do jednej z wartości: a) jedno z wyrażeo jest prawdą (#t) b) brak wyrażeo w ciele formy if (#f) LISP 9 Wyrażenia warunkowe • Wartośd wyrażenia or jest wartością ostatniego podwyrażenia - (a) • Wiele możliwych obiektów dla wartości prawdy • Zwykle, wartośd wyrażenia testowanego jest jedną z dwóch obiektów #t dla prawdy lub #f dla fałszu (< -1 0) #t (> -1 0) #f LISP 10 Wyrażenia warunkowe • Obiekty Scheme-a mogą przyjmowad wartośd prawdy lub fałszu: – w wyrażeniach warunkowych – w przypadku stosowania procedury not • Tylko obiekt #f posiada wartośd fałszu • Pozostałe obiektu mają wartośd prawdy (if #t 'true 'false) (if #f 'true 'false) LISP true false 11 Wyrażenia warunkowe (if '() 'true 'false) (if 1 'true 'false) (if '(a b c) 'true 'false) (not #t) (not "false") (not #f) #f #f #t (or) (or #f) (or #f #t) (or #f 'a #f) #f #f #t a LISP true true true 12 Wyrażenia warunkowe • and forma składniowa podobna do formy or • Wyrażenie and jest prawdą jeżeli wszystkie podwyrażenia mają wartośd #t • Wyrażenie (and) jest zawsze prawdziwe • Podwyrażenia są oceniane do jednej z wartości: – brak podwyrażeo w ciele formy - #t – wartośd podwyrażenia jest fałszem • Wartośd wyrażenia and jest wartością ostatniego ocenianego podwyrażenia LISP 13 Wyrażenia warunkowe • Przykład. Użycie and w funkcji reciprocal (define reciprocal (lambda (n) (and (not (= n 0)) (/ 1 n)))) (reciprocal 3) (reciprocal 0.5) (reciprocal 0) 1/3 2.0 #f Wartość - #f jeżeli n jest zerem w przeciwnym wypadku 1/n LISP 14 Wyrażenia warunkowe • Predykaty =, <, >, <=, >= • Predykat jest procedurą, która odpowiada na specyficzne pytania dotyczące argumentu i zwraca jedną z dwóch wartości #t or #f • Nazwy predykatów zakooczone są znakiem ( ? ) • Zwykłe predykaty relacji są wyjątkiem od powyższej reguły LISP 15 Wyrażenia warunkowe • Nie wszystkie predykaty wymagają argumentów w postaci liczb • Predykat null? jest prawdą jeżeli jego argument jest listą pustą () • Przykładowo: (null? (null? (null? (null? '()) 'abc) '(x y z)) (cdddr '(x y z))) LISP #t #f #f #t 16 Wyrażenia warunkowe • Problem z wyrażeniem(cdr '()) • Interpreter Scheme-z informuje o błędzie • Definicja lisp-cdr rozwiązuje ów problem (define lisp-cdr (lambda (x) (if (null? x) '() (cdr x)))) (lisp-cdr '(a b c)) (lisp-cdr '(c)) (lisp-cdr '()) LISP (b c) () () 17 Wyrażenia warunkowe • • • • Predykat eqv? wymaga dwóch argumentów Jeżeli dwa argumenty są równoważne prawda W przeciwnym wypadku eqv? zwraca fałsz Przykładowo: (eqv? (eqv? (eqv? (eqv? (eqv? (eqv? (eqv? 'a 'a) 'a 'b) #f #f) #t #t) #f #t) 3 3) 3 2) LISP #t #f #t #t #f #t #f 18 Wyrażenia warunkowe • Inne przykłady (let ((x "Hi Mom!")) (eqv? x x)) #t (let ((x (cons 'a 'b))) (eqv? x x)) #t (eqv? (cons 'a 'b) (cons 'a 'b)) #f LISP 19 Wyrażenia warunkowe • Predykaty typu zwracające prawdę lub fałsz: – pair?, symbol?, number?, string? • Predykat pair? zwraca prawdę tylko jeżeli jego argument jest parą (pair? (pair? (pair? (pair? '(a . c)) '(a b c)) '()) 'abc) (pair? "Hi Mom!") (pair? 1234567890) LISP #t #t #f #f #f #f 20 Wyrażenia warunkowe • Predykat typu jest użyteczny do określania czy stosowany argument jest odpowiedniego typu • Przykładowo: (define reciprocal (lambda (n) (if (and (number? n) (not (= n 0))) (/ 1 n) "oops!"))) (reciprocal 2/3) (reciprocal 'a) 3/2 "oops!" LISP 21 Wyrażenia warunkowe • Rozważmy inne wyrażenie warunkowe • cond często stosowane zamiast if • cond (podobne do if) pozwala na użycie wielu testów i alternatywnych wyrażeo • Wyrażenie cond posiada ogólną formę: (cond (test exp) ... (else exp)) LISP 22 Wyrażenia warunkowe • Rozważmy definicje funkcji sign zwracającej: – -1 dla ujemnych argumentów – +1 dla dodatnich argumentów – 0 dla zera (define sign (lambda (n) (if (< n 0) -1 (if (> n 0) +1 0)))) LISP 23 Wyrażenia warunkowe • Dwa wyrażenia if mogą byd zastąpione przez pojedyncze wyrażenie cond: (define sign (lambda (n) (cond ((< n 0) -1) ((> n 0) +1) (else 0)))) LISP 24 Wyrażenia warunkowe • Pominięcie else zwiększa przejrzystośd kodu • Nowa wersja funkcji sign (define sign (lambda (n) (cond ((< n 0) -1) ((> n 0) +1) ((= n 0) 0)))) • Definicja sign nie zależy od kolejności testowanych warunków LISP 25 Wyrażenia warunkowe • Przykładowa procedura obliczająca podatek dla danych dochodów w skali progresywnej z progami 10000, 20000, and 30000 euro (define income-tax (lambda (income) (cond ((<= income 10000) (* ((<= income 20000) (+ ((<= income 30000) (+ (else (+ (* (- income income .05)) (* (- income 10000) .08) 500.00)) (* (- income 20000) .13) 1300.00)) 30000) .21) 2600.00))))) LISP 26 Wyrażenia warunkowe • Przykładowe wywołania: (income-tax (income-tax (income-tax (income-tax 5000) 15000) 25000) 50000) 250.0 900.0 1950.0 6800.0 • Ważny kierunek przeprowadzania testu – od lewej do prawej i od góry do dołu LISP 27 Prosta rekurencja • Q: Jak wykonad powtórnie wyrażenie: – dla wszystkich elementów listy – dla liczb np. od 1 do 10? • A: Realizacja za pomocą rekurencji • Rekurencja jest prostą koncepcją zastosowania procedury w procedurze LISP 28 Prosta rekurencja • Rekurencyjna procedura jest procedurą zastosowaną do samej siebie • Najprostsza rekurencyjna procedura: (define goodbye (lambda () (goodbye))) (goodbye) • Procedura nie zwraca wyniku ze względu na nieskooczone wywołanie LISP 29 Prosta rekurencja • Rekurencyjne procedury powinny mied co najmniej dwa podstawowe elementy: – przypadek bazowy – krok rekurencyjny • Przypadek bazowy kooczy rekurencję dając procedurze wartośd • Krok rekurencyjny daje wartośd pod względem wartości zastosowanej procedury do innego argumentu LISP 30 Prosta rekurencja • Problem: znalezienie funkcji wyznaczającej długośd listy w sposób rekurencyjny • Argument bazowy rekursji to lista pusta • Długośd pustej listy wynosi zero • Przypadek bazowy powinien zwrócid zerową wartośd dla listy pustej • W celu zbliżania się do listy pustej krok rekurencji dotyczy ogona listy (cdr) LISP 31 Prosta rekurencja • Krok rekurencji daje wartośd o jeden więcej niż długośd ogona listy (cdr) (define length (lambda (ls) (if (null? ls) 0 (+ (length (cdr ls)) 1)))) (length '()) 0 (length '(a)) 1 (length '(a b)) 2 LISP 32 Prosta rekurencja • Śledzenie kolejnych kroków procedury rekurencyjnej length '(a b c d)) |(length (a b c d)) | (length (b c d)) | |(length (c d)) | | (length (d)) | | |(length ()) | | |0 | | 1 | |2 | 3 |4 LISP 33 Prosta rekurencja • Wcięcia reprezentują poziom zagnieżdżenia procedury • Linie pionowe odpowiadają graficznie wartościom wywołao rekurencyjnych • Każde wywołanie funkcji długośd powoduje zmniejszenie listy aż do listy pustej • Wartośd dla listy pustej wynosi 0 a następnie każdy zewnętrzny poziom dodaje 1 do otrzymania ostatecznej wartości LISP 34 Prosta rekurencja • Rozważmy procerdurę list-copy zwracająca kopie listy będącej jej argumentem • list-copy zwraca nową listę zawierającą elementy (nie pary) starej listy • Tworzenie kopi listy ma na celu zachowanie oryginału, który może podlegad dalej zmianie (list-copy '()) (list-copy '(a b c)) LISP () (a b c) 35 Prosta rekurencja • Definicja list-copy (define list-copy (lambda (ls) (if (null? ls) '() (cons (car ls) (list-copy (cdr ls)))))) • list-copy łączy głowę listy z wartościami rekurencyjnego wywołania LISP 36 Prosta rekurencja • Możliwośd zdefiniowania więcej niż jednego przypadku bazowego • Procedura memv ma 2 argumenty - obiekt i lista • memv zwraca: – podlistę (ogon) którego pierwszy element jest równy obiektowi – #f jeżeli obiekt nie został znaleziony • Wartości memv - używane jako lista lub jako wartośd prawdy w wyrażeniach warunkowych LISP 37 Prosta rekurencja (define memv (lambda (x ls) (cond ((null? ls) #f) ((eqv? (car ls) x) ls) (else (memv x (cdr ls)))))) • Pierwszy cond testuje bazowy przypadek () Obiekt nie jest elementem listy #f • Drugi cond pyta czy głowa listy jest obiektem • Krok rekurencji kontynuuje sprawdzanie dla pozostałych elementów LISP 38 Prosta rekurencja • Przykładowe wywołanie: (memv 'a '(a b b d)) (a b b d) (b b d) #f (d) (memv 'b '(a b b d)) (memv 'c '(a b b d)) (memv 'd '(a b b d)) (if (memv 'b '(a b b d)) "yes" no") "yes" LISP 39 Prosta rekurencja • Procedura remv posiada dwa argumenty obiekt i listę • remv zwraca nową listę obiektów bez ich powtórzeo (define remv (lambda (x ls) (cond ((null? ls) '()) ((eqv? (car ls) x) (remv x (cdr ls))) (else (cons (car ls) (remv x (cdr ls) )))))) LISP 40 Prosta rekurencja (remv (remv (remv (remv 'a 'b 'c 'd '(a '(a '(a '(a b b b b b b b b d)) d)) d)) d)) (b (a (a (a b d) d) b b d) b b) • remv nie zatrzymuje się tylko na elementach głowy listy – kontynuuje rekurencję poprzez zignorowanie obiektów pojedynczych • Jeżeli obiekt nie został znaleziony jako car listy, remv łączy ten element z rekurencyjną wartością LISP 41 Prosta rekurencja • Inna wersja procedury tree-copy traktującej strukturę drzewa argumentu jako parę (define tree-copy (lambda (tr) (if (not (pair? tr)) tr (cons (tree-copy (car tr)) (tree-copy (cdr tr)))))) (tree-copy '((a . b) . c)) ((a . b) . c) LISP 42 Prosta rekurencja • Lewe podrzewo jest głową pary • Prawe podrzewo jest ogonem pary • Podobna operacja do list-copy budująca nowe pary pozostawiając tylko liście • Przypadek bazowy sprawdza czy elementem drzewa jest wszystko poza parą • Krok rekurencyjny jest podwójnie rekursywny znajdujący wartośd rekurencyjnie dla głowy jak i ogona argumentu LISP 43 Prosta rekurencja • Iteracje w Scheme-a wyrażone za pomocą rekurencji są bardziej czytelne i treściwe • Rekurencja jest eliminuje dodatkowe zmienne i przypisania wymagane w innych językach zawierające struktury iteracyjne • W rezultacie kod programu jest bardziej niezawodny i łatwiejszy do śledzenia • Niektóre rekurencje są w istocie iteracją LISP 44 Prosta rekurencja • Rozważmy specjalną formę składniową powtórzeo zwanych odwzorowaniem • Procedura modulus-all posiada argument będący listą liczb całkowitych i zwraca ich moduł (define modulus-all (lambda (ls) (if (null? ls) '() (cons (modulus (car ls)) (modulus-all (cdr ls)))))) (abs-all '(1 -2 3 -4 5 -6)) (1 2 3 4 5 6) LISP 45 Przypisanie • Przypisanie nie tworzy nowego związania jak wyrażenia let lub lambda • Przypisanie zmienia wartośd istniejącego związania • Przypisanie realizowane wyrażeniem set! (define abcde '(a b c d e)) abcde (a b c d e) (set! abcde (cdr abcde)) abcde (b c d e) LISP 46