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