Wykład 8

Transkrypt

Wykład 8
PARADYGMATY I JĘZYKI PROGRAMOWANIA Programowanie funkcyjne (w-­‐8) Treść 2 ¨ 
Python – wprowadzenie klasy ¤  podprogramy ¤  generatory ¤  iteratory ¤  funkcja lambda ¤  funkcje apply, map, filter, reduce, eval etc. ¤ 
¨ 
Programowanie funkcjonalne – wstęp WFPM ¤  pierwiastek kwadratowy z liczby ¤  obliczanie pochodnej ¤  całkowanie ¤ 
w-­‐8 3 Python w skrócie (Patrz D.M. Beazley: Python. EssenZal reference, Sams 2006) w-­‐8 Python funkcyjny 4 ¨ 
Python ¤ 
funkcje i struktury, które można wykorzystać w programowaniu funkcyjnym; możliwość rekurencji n 
n 
n 
n 
n 
n 
n 
n 
n 
n 
n 
n 
n 
w-­‐8 list lista tuple krotka (n-­‐tka) lambda lambda args : expression
apply apply(funcname, [, args [, kwargs]])
eval a = eval(‘3*math.sin(3.5+x) + 7.2’)
map b = map(lambda x: 3*x, a)
zip zszywanie wyrażeń iterowalnych (list, krotek) reduce b = reduce(sum, a)
filter c = filter(lambda x: x < 4, a)
reverse, extend, insert, pop, remove, count, sort, ... iteratory generatory (yield) wyrażenia generatorowe Python w skrócie 5 ¨ 
funkcje, argumenty, zwracane wartości – wiele def factor(a):
d = 2
while (d <= (a/2)):
if ((a/d)*d == a):
return ((a/d),d)
d = d + 1
return (a,1)
¨ 
Wywołanie x, y
= factor(1234)
(x, y) = factor(1234)
w-­‐8 Python – klasy 6 ¨ 
Definicja class Circle(object):
def __init__(self,radius):
self.radius = radius
def getArea(self):
return math.pi*self.radius**2
def setArea(self,area):
self.radius =
math.sqrt(area/math.pi)
area =
property(getArea, setArea,
doc=’area of circle’)
¨ 
¨ 
w-­‐8 Tworzenie obiektów, dostęp a=Circle(rad);
print a.getArea()
dziedziczenie Python – klasy 7 Dziedziczenie class A(object):
¨ 
def method1(self):
print “Class A : method1”
class B(A): # Inherits from A
def method1(self):
print “Class B : method1”
def method2(self):
print “Class B : method2”
class C(B): # Inherits from B
def method3(self):
print “Class C: method 3”
class D(A):
def method1(self):
print “Class D: method 1”
class E(B,D): # Dziedziczy od B i D
#(dziedziczenie wielokrotne)
pass
w-­‐8 c = C()
# Tworzenie egz. ‘C’
c.method3() # Wywołanie C.method3(c)
c.method1() # Wywołanie B.method1(c)
e = E()
# Tworzenie egz. ‘E’
e.method1() # Wywołanie B.method1(e) Python – generatory 8 ¨ 
Generatory i yield. Generator jest funkcją, która produkuje ciąg wyników zamiast jednej wartości def countdown(n):
while n > 0:
yield n
n -= 1
>>> for i in countdown(5):
... print i,
...
5 4 3 2 1
>>>
¨ 
w-­‐8 Generowanie ciągu wartości zapewnia instrukcja yield
Python w skrócie 9 ¨ 
¨ 
inne zachowanie niż w przypadku zwykłej funkcji wywołanie generatora tworzy obiekt generatora, ale go nie uruchamia def countdown(n):
print ”Odliczanie w dół od", n
while n > 0:
yield n
n -= 1
>>> x = countdown(10)
>>>
>>> x
<generator object at 0x58490>
>>>
w-­‐8 Definicja generatora Tworzenie generatora ... nic się nie dzieje Informacje o obiekcie Python w skrócie 10 ¨ 
¨ 
¨ 
w-­‐8 Generator uruchamia metoda next()
>>> x.next()
Odliczanie w dół od 10
10
>>>
yield oblicza wartość i zawiesza wykonanie Funkcja wznawia wykonanie po następnym wywołaniu metody next()
>>> x.next()
9
>>> x.next()
8
...
>>> x.next()
1
>>> x.next()
Traceback (most recent call last): File "<stdin>", line 1, in ? StopIteraZon >>>
Python w skrócie 11 ¨ 
Generatory – przetwarzanie potokowe wejście generator generator ... generator for x in s Idea: ciąg generatorów przetwarza sekwencję s z pomocą pętli for
¨  Tworzenie współprogramów (zaawansowane!, patrz Internet) z użyciem metody send()
¨  Filtry ¨ 
w-­‐8 12 Programowanie funkcyjne (PF) Wstęp w-­‐8 Problemy 13 Funkcje ¨  Modularyzacja ¨  Rekurencja ¨  Sklejanie ¨  Leniwe obliczanie ¨ 
¨ 
Literatura: ¤  Why funcZonal programming malers. J. Hughes. ¤  StackOvelflow (wfpm; programy w Haskell, Scheme) w-­‐8 Co to jest PF? 14 ¨ 
¨ 
Nazwa PF pochodzi stąd, że podstawową operacją jest aplikowanie funkcji do argumentów. Główny program jest funkcją, która otrzymuje na wejściu swoje argumenty, przetwarza je i podaje wynik obliczeń, który też może być funkcją. Program zbudowany jest z innych funkcji, te jeszcze z innych itd. aż do momentu gdy funkcje stają się podstawowymi elementami języka. Dwie główne cechy PF ¤  Funkcje wyższego rzędu ¤  Leniwe obliczanie w-­‐8 Co to jest PF? 15 ¨ 
Charakterystyki PF – potoczne ¤  Brak instrukcji przypisania – zmienne raz określone nie zmieniają sią ¤  Brak efektów ubocznych – funkcje obliczają tylko siebie à eliminacja błędów à nie jest ważna kolejność obliczeń (ponieważ efekty uboczne nie zmieniają wartości wyrażeń można je obliczać w dowolnym czasie) à zmienne można więc zastępować ich wartościami i odwrotnie – programy są referencyjnie transparentne ¤  Programy są wielokrotnie krótsze niż te w językach imperatywnych à programiści są więc bardziej efektywni niż programiści, stosujący języki imperatywne w-­‐8 Co to jest PF? 16 ¨ 
¨ 
w-­‐8 Analogia z definicją programów strukturalnych, podobna charakterystyka przez wskazanie cech, których brak np. brak goto, bloki mają pojedyncze wejście i wyjście, bardziej matematyczna struktura. Główna różnica między programowaniem strukturalnym i niestrukturalnym to MODULARNOŚĆ, która zapewnia dużą wydajność w procesie programowania; moduły mogą być wielokrotnie używane; testowanie programów jest prostsze; łatwiej zlokalizować błędy; prostsza kompilacja – częściowa. Zapomniana cecha języków strukturalnych: najpierw dzielimy problem na podproblemy, a następnie je ze sobą sklejamy w całość! PF – Odpowiednia modularyzacja i odpowiednie składanie prostych części (funkcji) w całość – sklejanie funkcji; moduły można wykorzystać powtórnie Modularyzacja i sklejanie – przykład 17 ¨ 
¨ 
Definicja listy (cons jest tutaj funkcją składania) list X := nil | cons X (list X)
[]
oznacza nil, lista pusta; [1] oznacza cons 1 nil;
[1,2,3] oznacza cons 1 (cons 2 (cons 3 nil)) Sumowanie sum nil = 0
sum (cons numb list) = numb + sum list
lista
sum
sum można zmodularyzować, wprowadzając operację reduce: sum = reduce add 0
gdzie add jest funkcją dwuargumentową: add x y = x + y
Wstawiając definicję funkcji sum otrzymamy rekurencyjną definicję reduce: (reduce add x) nil = x
(reduce add x) (cons a l) = add a ((reduce add x) l)
(wyrażenie w nawiasach zastąpiło sum; nawiasy można opuścić) w-­‐8 add
reduce
Pewne funkcje 18 ¨ 
¨ 
¨ 
w-­‐8 Można uogólnić ostatnią formułę zamieniając add na f: reduce f x nil = x
reduce f x (cons a l) = f a (reduce f x l)
Funkcja trójargumentowa (tutaj reduce), zastosowana do dwu argumentów jest traktowana, jak funkcja tylko trzeciego argumentu; w ogólności funkcja n argumentowa działając na m<n argumentów staje się funkcją m-n
argumentową. Możemy teraz (bez dadatkowego programowania) użyć reduce do obliczenia iloczynu (lub zbadać czy lista zawiera element true, czy też wszystkie elementy true): product = reduce multiply 1
anytrue = reduce or false
alltrue = reduce and true
Pewne funkcje 19 ¨ 
¨ 
¨ 
¨ 
¨ 
reduce można też rozumieć jako operację, która w liście zastępuje cons przez f, a nil przez a: Lista [1,2,3] oznacza:
cons (1 cons (2 cons 3 nil)))
Operacja
reduce add 0
powoduje zamianę listy na:
add (1 add (2 (add 3 0))) = 6
(zamiana cons -> add, nil -> 0) Podobnie reduce multiply 1 daje: multiply (1 multiply (2 (multiply 3 1))) = 6
Widać również, że reduce cons nil kopiuje listę. Ponieważ do listy można dodać inną listę przez operację cons, to widać też, że następująca funkcja append dodaje elementy do listy: append a b = reduce cons b a
w-­‐8 Pewne funkcje 20 ¨ 
Sprawdzimy na przykładzie: append [1,2] [3,4] = reduce cons [3,4] [1,2]
= (reduce cons [3,4]) (cons 1 (cons 2 nil))
= cons 1 (cons 2 [3,4]))
(zastąpiono cons przez cons, a nil przez [3,4]) = [1,2,3,4]
¨ 
¨ 
Funkcja, która podwaja elementy listy może być zapisana jako doubleall = reduce doubleandcons nil
gdzie doubleandcons num list = cons (2*num) list
Funkcję doubleandcons można jeszcze bardziej zmodularyzować:
po pierwsze jako doubleandcons = fandcons double double n = 2*n
fandcons f el list = cons (f el) list
i następnie
fandcons f = cons . f
gdzie “.” oznacza złożenie funkcji f.g h = f(g h)
w-­‐8 Pewne funkcje 21 ¨ 
Sprawdźmy poprawność fandcons: fandcons f el = (cons . f) el = cons (f el)
czyli fandcons f el list = cons (f el) list
¨ 
Końcowa postać doubleall: doubleall = reduce (cons . double) nil
¨ 
Kolejna modularyzacja prowadzi do doubleall = map double
map f = reduce (cons . f) nil
gdzie map stosuje dowolną funkcję f do wszystkich elementów listy. w-­‐8 Ogólny program obliczeń w PF 22 ¨ 
f – program; f(dane)
Jeśli program f dostarcza danych do programu g, to cały program obliczeń ma postać: g(f(dane))
¨  Mogłoby się zdarzyć, że f produkuje tak dużo danych, że nie ma miejsca na ich przechowywanie. Jest jednak tak, że program f dostarcza dane na żądanie, a więc oblicza ich tyle ile potrzebuje g – to właśnie nazywamy leniwym obliczaniem. ¨ 
w-­‐8 Przykład 23 ¨ 
Algorytm Newtona (Herona) dla pierwiastka kwadratowego z liczby N ¤ 
¤ 
¤ 
¨ 
¨ 
w-­‐8 Pierwsze przybliżenie: x = a (jakieś)
następne: x = ½ (x+N/x)
i kolejne: xn+1 = ½ (xn+N/xn
¨ 
¨ 
Pole
N
„dowód”(lepszy; szereg Taylora) xn+1 = (xn + N/xn)/2
Jeśli w granicy mamy zbieżność, tzn. xn -> a: a =(a+N/a)/2
czyli
2a = a+N/a, a = N/a
a*a = N
stąd a = squareroot(N)
Algorytm Herona-­‐Newtona-­‐
Raphsona sqrt(N): Dane jest pole N kwadratu. Obliczyć bok a. a
¨ 
N/a
Ponieważ oba boki o długościach a, N/a są przybliżeniem, więc ich średnia wartość (średnia arytmetyczna) jest bliższa prawdy... Przykład cd. 24 ¨ 
¨ 
w-­‐8 Program (Python) def squareroot(N,a,eps=1e-6):
x = a
y = a+2*eps
while abs(x-y)>eps:
y = x
x=1/2*(x+N/x)
return x
Program ten, zapisany w imperatywnym języku, nie da sie podzielić na mniejsze jednostki (?) Przykład cd. 25 ¨ 
To samo w języku funkcyjnym ¤  będziemy generować kolejne przybliżenia funkcją next N x = (x + N/x) / 2
¤  Jeśli oznaczymy tę funkcję przez f, to ciąg kolejnych przybliżeń jest postaci (list): [a, f a, f(f a), f(f(f a)), ...]
¤  zdefiniujemy funkcję, która generuje ten ciąg: repeat f a = cons a (repeat f (f a))
¤  listę kolejnych przybliżonych wartości pierwiastka obliczymy następująco repeat (next N) a
w-­‐8 Przykład cd. 26 ¨ 
¨ 
repeat jest przykładem funkcji o nieskończonej liczbie wyników ale ponieważ będziemy potrzebować tylko tyle wyników ile wymaga dokładność obliczeń, węc chcemy by repeat generowało kolejne przybliżenia na żądanie, a więc leniwie (nie wszystko od razu) Aby ten efekt uzyskać zdefiniujemy funkcję within, która będzie kontrolować dokładność obliczeń (parametr eps) within eps (cons a (cons b rest)) =
= b, if abs(a-b) <= eps
= within eps (cons b rest), if not
¨ 
w-­‐8 Sklejając wszystko, mamy następujący program: sqrt a eps N = within eps (repeat (next N) a)
Python – squareroot
27 def next(N,a):
while True:
¨ 
a = (a + N/a) / 2.0
my_sqrt(4, 1, 1e-6)
print x, x*x # => 2
yield a
def within(eps,generator,*args):
g = generator(*args)
a,b = g.next(), g.next()
while abs(a-b) > eps:
a,b = b, g.next()
return b
def my_sqrt(N,guess,eps):
return
within(eps,next,N,guess)
w-­‐8 Program obliczający ¨ 
Nie jest to czysto funkcyjny program (!) ale posiada cechy programowania funkcyjnego (leniwa ewaluacja, listy, generatory) Scheme – squareroot
28 (require (lib "stream.ss" "srfi" "40"))
(define (next_c N)
(lambda (a)
(/ (+ a (/ N a)) 2.0)))
(define (repeat f init)
(stream-cons init (repeat f (f init))))
(define (within eps stream)
(let ((a (stream-car stream))
(b (stream-car (stream-cdr stream))))
(if (<= (abs (- a b)) eps)
b
(within eps (stream-cdr stream)))))
(define (my-sqrt guess eps N)
(within eps (repeat (next_c N) guess)))
w-­‐8 Haskell – squareroot 29 -- 4.1 Newton-Raphson square roots
next n x = (x + n/x)/2.0
-- -- this is "iterate::(a->a)->a->[a]"
-- repeat f a
= a : iterate f (f a)
within eps (a:b:rest) =
if abs(a-b) <= eps
then b
else within eps (b:rest)
sqroot a eps n = within eps (iterate (next n) a)
relative eps (a:b:rest) =
if abs(a-b) <= eps*abs(b)
then b
else relative eps (b:rest)
relativesqrt a eps n = relative eps (iterate (next n) a)
w-­‐8 Przykład. Python. Pochodna f(x)
30 Dysponując wcześniej zbudowanymi funkcjami możemy obliczać np. pochodne, całki itp. z funkcji, z zadaną dokładnością (eps) ¨  W pierwszym przybliżeniu, pochodna numeryczna funkcji f(x) jest dana jako iloraz różnicowy df/dx ≈ (f(x+h) –f(x))/h ¨  Dla rozsądnych h (krok) można to poprawić, licząc df/dx dla kroku mniejszego, h/2. ¨  Proces powtarzamy aż do uzyskania zadanej dokładności obliczeń eps ¨ 
w-­‐8 Program (schemat funkcyjny) 31 easydiff f x h = (f(x+h)-f x) / h
differentiate h0 f x =
map (easydiff f x) (repeat halve h0)
halve x = x/2
¨ 
obliczenia z dokładnością eps:
within eps (differentiate h0 f x)
w-­‐8 Program – lepsza wersja 32 ¨ 
¨ 
Kolejne przybliżenia an, an+1 zawierają błędy postaci B*h**n, B – stała (Wynika to z analizy szeregu Taylora) Mamy więc równania (dla kolejnych wartości 2h i h): a n = A + B × 2n × h n
an+1 = A + B × hn
Stąd, poprawiona wartość wyniku A jest równa: n
2 an+1 an
A=
2n 1
Nie znamy n. Jak obliczyć n? w-­‐8 cd 33 Wartość n (nie będziemy tego dowodzić; z trzech kolejnych wyników dla kolejnych n; patrz algorytmy):
n(a, b, c) = int log2
✓
a
b
c
c
◆
1
order (cons a (cons b (cons c rest)))
= round(log2( (a-c)/(b-c) - 1 )) w-­‐8 cd 34 ¨ 
¨ 
¨ 
¨ 
w-­‐8 Tutaj round x
log2 x
oznacza zaokrąglenie x do najbliższej liczby całkowitej jest logarytmem o podstawie 2 z x
Błędy częściowo eliminuje więc funkcja: elimerror n (cons a (cons b rest)) =
= cons ((b*(2**n)-a)/(2**n-1))
(elimerror n (cons b rest))
Ciąg poprawionych przybliżeń dostaniemy z: improve s = elimerror (order s) s
I pochodna jest dana przez: within eps (improve (differentiate h0 f x))
cd 35 ¨ 
¨ 
¨ 
¨ 
w-­‐8 Lepsze przybliżenie dostaniemy, powtarzając improve within eps (improve (improve (improve
(differentiate h0 f x))))
Zdefiniujmy funkcję: super s = map second (repeat improve s)
second (cons a (cons b rest)) = b
Ostatecznie możemy zapisać: within eps (super (differentiate h0 f x))
mamy coraz lepsze i lepsze przyblżenia... Opisany algorytm jest bardzo złożony i w Pythonie jest o wiele bardziej skomplikowany itd. Python – algorytm Newtona 36 def next(N,a):
while True:
a = (a + N/a) / 2.0
def my_sqrt(N,guess,eps):
return
within(eps,next,N,guess)
yield a
x=my_sqrt(4, 1, 1e-10)
def within(eps,generator,*args):
g = generator(*args)
a,b = g.next(), g.next()
while abs(a-b) > eps:
a,b = b, g.next()
return b
w-­‐8 print x, x*x
Python (dokładniej) 37 from math import log, floor, sin, cos
def within(eps,generator,*args):
g = generator(*args)
def halve(x):
a,b = g.next(),g.next()
return x/2
while abs(a-b) > eps:
a,b = b,g.next()
def round(x):
return b
return floor(x+0.5)
def differentiate(function,x,eps):
def order(a,b,c):
h=100*eps
return round(log((a-c)/(b-c)-1, 2))
# generator
def easydiff(function,x,h):
return
within(eps,easydiff,function,x,h)
if __name__=='__main__':
eps=0.0001;
while True:
print x,differentiate(cos,x,eps),
-sin(x)
diff=(function(x+h)-function(x))/h
w-­‐8 x=2.
h=halve(h)
yield diff
Zadania: pochodne i całkowanie 38 ¨ 
¨ 
¨ 
¨ 
w-­‐8 Proszę napisać program poprawionego obliczania pochodnej w Pythonie Proszę napisać program całkowania funkcji f(x) na przedziale [a,b] metodą trapezów lub metodą Simpsona z wykorzystaniem funkcji, które zdefiniowaliśmy na wykładzie (w języku quasi-­‐
funkcjonalnym) Ulepszyć ten program całkowania, wykorzystując funkcję ellimerroer
Napisać funkcję flatten, która dowolną listę (krotkę) przekształca w listę bez elementów iterowalnych (w skalary) za tydzień ... !? 39 w-­‐3 w-­‐8 06.03.2013 12:04 Haskell – algorytm Newtona 40 -- Newton-Raphson square roots
next n x = (x + n/x)/2.0
-- -- this is "iterate::(a->a)->a->[a]”
-- repeat f a = a : iterate f (f a)
within eps (a:b:rest) =
if abs(a-b) <= eps
then b
else within eps (b:rest)
sqroot a0 eps n = within eps (iterate (next n) a0)
relative eps (a:b:rest) =
if abs(a-b) <= eps*abs(b)
then b
else relative eps (b:rest)
w-­‐8 relativesqrt a0 eps n = relative eps (iterate (next n) a0)
Haskell – pochodna 1 41 easydiff f x h = (f (x+h) - f x) / h
differentiate h0 f x = map (easydiff f x) (iterate (/2) h0)
-- diff1a h0 eps f x = within eps (differentiate h0 f x)
diff1 h0 eps f = within eps . differentiate h0 f
elimerror n (a:b:rest) = (b*(2**n)-a)/(2**n-1) : elimerror n (b:rest)
-- need fromIntegral to make a non-integer out of the Int which comes out
of round
order (a:b:c:rest) =
w-­‐8 fromIntegral (round (logBase 2 ((a-c)/(b-c)-1)))
Haskell – pochodna 2 42 improve s = elimerror (order s) s
--diff2a h0 eps f x = within eps (improve (differentiate h0 f x))
diff2 h0 eps f = within eps . improve . differentiate h0 f
-- super s = map second (iterate improve s)
-- how can we make this point-free?
super :: (RealFrac t, Floating t) => [t] -> [t]
-- w/o this it wants to be [double]->[double]
super = map second . iterate improve
-- second (a:b:rest) = b
second = head . tail
diff3 h0 eps f = within eps . super . differentiate h0 f
w-­‐8 Haskell – całka 43 -- integration
easyintegrate f a b = (f a + f b)*(b-a)/2
-- addpair becomes (uncurry (+))
integrate f a b = integ f a b (f a) (f b)
integ f a b fa fb =
(fa+fb)*(b-a)/2 : map (uncurry (+)) (zip (integ f a m fa fm) (integ f m
b fm fb))
where m = (a+b)/2
fm = f m
-- test: following should be about pi
approxpi eps = within eps (improve (integrate (\x -> 4/(1+x*x)) 0 1))
superpi eps = within eps (super (integrate (\x -> 4/(1+x*x)) 0 1))
w-­‐8 Scheme 44 (require (lib "stream.ss" "srfi" "40"))
(define (within eps stream)
(let ((a (stream-car stream))
(define (next_c N)
(b (stream-car (stream-cdr stream))))
(lambda (a)
(if (<= (abs (- a b)) eps)
(/ (+ a (/ N a)) 2.0)))
b
(within eps (stream-cdr stream)))))
(define (repeat f init)
(stream-cons init (repeat f (f init))))
(define (my-sqrt guess eps N)
(within eps (repeat (next_c N) guess)))
w-­‐8 

Podobne dokumenty