Informatyka Wykład 6 2017/2018z Bogumil.Konopka@pwr.edu.pl
Plan Rekurencja Programowanie zorientowane obiektowo Zalety programowania obiektowego Korzystanie z obiektów Projektowanie własnej klasy Przeładowanie funkcji
Rekurencja - rekursja Odwoływanie się przez funkcję lub definicję do samej siebie Ciąg Fibonacciego: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, F 0 = 0 F 1 = 1 F n = F n 1 + F n 2, n 2 Silnia: 1, 1, 2, 6, 24, 120, 720, 0! = 1 n! = n n 1!, n 1
Podstawowe elementy funkcji rekurencyjnej Przypadek bazowy silnia N = 1 n = 0 n n 1!, n 1 Zmiana w kierunku przypadku bazowego Wywołanie rekurencyjne Liczenie silni: wartosc = silnia(n) { Jeżeli ( ) wartosc = 1 W przeciwnym wypadku wartosc = } Co w pustych polach?
Podstawowe elementy funkcji rekurencyjnej Przypadek bazowy silnia N = 1 n = 0 n n 1!, n 1 Liczenie silni: Zmiana w kierunku przypadku bazowego Wywołanie rekurencyjne wartosc = silnia(n) { Jeżeli (n == 0) wartosc = 1 W przeciwnym wypadku wartosc = n*silnia(n-1) }
Silnia liczona rekurencyjnie - przykład silnia(5)
Silnia liczona rekurencyjnie - przykład silnia(5) Czy 5 == 0? wartosc = 5*silnia(5-1) Czy 4 == 0? wartosc = 4*silnia(3) wartosc = silnia(n) { Jeżeli (n == 0) wartosc = 1 W przeciwnym wypadku wartosc = n*silnia(n-1) } Czy 3 == 0? wartosc = 3*silnia(2) Czy 2 == 0? wartosc = 2*silnia(1) Czy 1 == 0? wartosc = 1*silnia(0) wartosc = 1 Czy 0 == 0?
Czasami rekurencję można zastąpić pętlą %Obliczenie silni N=5 s=1 for n = 1:N s=s*n; %Fibonacci fib(1)=0 fib(2)=1 for n=3:n fib(n)=fib(n-2)+fib(n-1) s=prod(1:n) s=factorial(n)
Jakie operacje są wykonywane w czasie wykonywania funkcji? Wywołanie funkcji Na stosie rezerwowane jest miejsce dla wartości zwracanych przez funkcję oraz jej argumentów wejściowych Argumenty wejściowe są kopiowane w zarezerwowane miejsce Następuje przeskok do wywołanej funkcji Realizacja kodu funkcji Wynik funkcji jest kopiowany do zmiennych zwracanych Ze stosu usuwane są zmienne zakończonej funkcji Następuje powrót do miejsca wywołania funkcji Każda z czynności zajmuje czas!
Kiedy używać rekurencji? Gdy kod rekurencyjny jest bardziej zrozumiały Np. bardziej zgodny ze znaną definicją Gdy mamy do czynienia z zagnieżdżonymi strukturami danych Gdy złożony problem może być podzielony na serię prostszych problemów rozwiązywanych po sobie Przykład rozwiązywanie shinoku
Shinoku - zasady Pole gry macierz 4x4 Pole podzielona jest na 4 kratki 2x2 Pole wypełniane jest liczbami 1:4 Zasady wypełniania: Liczba występuje tylko raz w każdym wierszu, kolumnie, kratce 2x2 1 3 4 2 2 4 3 1 3 2 1 4 4 1 2 3
Shinoku algorytm rozwiązania Jeżeli nie ma pustych pól to zakończ Przejdź do pierwszego pustego pola Próbuj kolejne liczby 1:4 Jeżeli liczba nie powoduje konfliktu, spróbuj uzupełnić następne pole Jeżeli nie ma możliwości wpisu, cofnij się w wypełnianiu i zmień wybór.
Implementacja w MATLABIE Jeżeli nie ma pustych pól to zakończ Przejdź do pierwszego pustego pola Próbuj kolejne liczby 1:4 Jeżeli liczba nie powoduje konfliktu, spróbuj uzupełnić następne pole Jeżeli nie ma możliwości wpisu, cofnij się w wypełnianiu i zmień wybór.
Implementacja w MATLABIE function rx = Shinoku(X) [w,k]=find(~x,1,'first'); % szukaj pierwszego zera if (isempty(w)) rx=x; return %jezeli cala macierz jest wypelniona to zwroc wynik % Jeżeli nie ma pustych pól to zakończ Przejdź do pierwszego pustego pola Próbuj kolejne liczby 1:4 Jeżeli liczba nie powoduje konfliktu, spróbuj uzupełnić następne pole Jeżeli nie ma możliwości wpisu, cofnij się w wypełnianiu i zmień wybór.
Implementacja w MATLABIE for proba=1:4 X(w,k)=proba if (SprawdzPoprawnosc(X(w,:)) &&... SprawdzPoprawnosc(X(:,k)) &&... SprawdzPoprawnosc(X(kratkaX,kratkaY))) rx=shinoku(x); %jezeli rx wrocilo kompletne to zwroc dalej %w przeciwnym wypadku kontynuj petle "for" if (isempty(find(~rx,1,'first'))) return X(w,k)=0; rx=x; Jeżeli nie ma pustych pól to zakończ Przejdź do pierwszego pustego pola Próbuj kolejne liczby 1:4 Jeżeli liczba nie powoduje konfliktu, spróbuj uzupełnić następne pole Jeżeli nie ma możliwości wpisu, cofnij się w wypełnianiu i zmień wybór.
Implementacja w MATLABIE for proba=1:4 X(w,k)=proba if (SprawdzPoprawnosc(X(w,:)) &&... SprawdzPoprawnosc(X(:,k)) &&... SprawdzPoprawnosc(X(kratkaX,kratkaY))) rx=shinoku(x); %jezeli rx wrocilo kompletne to zwroc dalej %w przeciwnym wypadku kontynuj petle "for" if (isempty(find(~rx,1,'first'))) return X(w,k)=0; rx=x; Jeżeli nie ma pustych pól to zakończ Przejdź do pierwszego pustego pola Próbuj kolejne liczby 1:4 Jeżeli liczba nie powoduje konfliktu, spróbuj uzupełnić następne pole Jeżeli nie ma możliwości wpisu, cofnij się w wypełnianiu i zmień wybór.
Rekurencja - podsumowanie Funkcja rekurencyjna musi mieć: Przypadek bazowy trywialne, natychmiastowe rozwiązanie Modyfikację w kierunku przypadku bazowego Wywołanie samej siebie ze zmodyfikowanymi parametrami wejściowymi Z rekurencji korzysta się, gdy: Problem można rozbić na ciągłe rozwiązywanie serii mniej skomplikowanych problemów Mamy do czynienia z drzewiastą strukturą danych Czytelność kodu jest większa niż w implementacji z pętlą iteracyjną
Programowanie proceduralne vs programowanie zorientowane obiektowo Programowanie Reprezentacja Danych Implementacja operacji Programowanie proceduralne Programowanie zorientowane obiektowo Dane Dane2 Dane3 Funkcja1 Funkcja2 Funkcja3 Dane2 Dane3 Dane4 Atrybut1 Metoda1 Atrybut2 Metoda2 Atrybut3 Metoda3
Programowanie proceduralne Dane Funkcja1 Dane2 Funkcja2 Dane3 Akcent położony jest na identyfikacji czynność, które muszą być wykonane Dane są zazwyczaj pojedynczymi zmiennymi. Funkcje wykonują jedną, bądź wiele operacji na danych
Programowanie zorientowane obiektowo 1. Identyfikacja elementów systemu, który zamierzamy zbudować 2. Identyfikacja schematów które elementy są używane wielokrotnie, które elementy mają wspólne cechy 3. Klasyfikacja elementów w oparciu o podobieństwa i różnice. 4. Implementacja klas, które zdefiniują obiekty używane w aplikacji Atrybut1 Metoda1 Atrybut2 Metoda2 Jaka jest różnica między klasą, a obiektem? http://www.mathworks.com/help/matlab/matlab_oop/why-use-object-oriented-design.html#brk75so
Gdzie mieliśmy do czynienia z obiektami? Wiele z funkcji w MATLABIE korzysta z obiektów np.: Dopasowywanie krzywych [g, gof, out ] = fit(x',y_exp',ftype) >> g g = General model: g(x) = 1/(1+exp(-b*x)) Coefficients (with 95% confidence bounds): b = 0.7388 (0.6541, 0.8235) Obsługa błędów za pomocą try/catch
Przykład - Obsługa błędów obiekt MException function [wynik,err] = MnozenieMacierzy(A,B) err=[]; try wynik=a*b catch err %zlap wyjątek err %wyświetl obiekt err sa=size(a); %pobierz rozmiary A sb=size(b); %pobierz rozmiary B warning('rozmiary A: %d x %d, Rozmiary B: %d x %d', sa, sb) wynik=[]; >> [wynik,err] = MnozenieMacierzy([2 1],[3 1]) Warning: Rozmiary A: 1 x 2, Rozmiary B: 1 x 2 > In MnozenieMacierzy at 8 wynik = [] err = MException with properties: identifier: 'MATLAB:innerdim' message: 'Inner matrix dimensions must agree.' cause: {0x1 cell} stack: [1x1 struct] >> class(err) ans = MException
Obiekty porządkują przechowywanie danych Dane są przechowywane jako atrybuty/właściwości obiektu >> properties(err) Properties for class MException: identifier message cause stack Do pól odnosimy się przez operator. >> err.stack ans = file: 'C:\Users\Bob\Documents\MATLAB\Inf_2014_2015\MnozenieMacierzy.m' name: 'MnozenieMacierzy' line: 4 >> class(err.stack) ans = struct
Obiekty same obsługują swoje dane Obiekt zawiera w sobie definicje operacji, które mogą być przeprowadzone na danych metody definiowane dla klasy. >> methods(err) Methods for class MException: addcause getreport ne throw eq isequal rethrow throwascaller Static methods: last >> err.getreport ans = Error using * Inner matrix dimensions must agree. Error in MnozenieMacierzy (line 4) wynik=a*b
Obiekty same obsługują swoje dane (2) Obiekt zawiera w sobie definicje operacji, które mogą być przeprowadzone na danych metody definiowane dla klasy. >> [wynik,err2]=mnozeniemacierzy([1 2 3 4], [2 3]'); Warning: Rozmiary A: 1 x 4, Rozmiary B: 2 x 1 > In MnozenieMacierzy at 8 >> err.isequal(err2) % uruchomienie metody isequal na obiekcie err ans = 1 >> err==err2 %przeladowanie operatora == ans = 1
Kiedy należy rozważyć zastosowanie programowanie obiektowe w MATLABie? Proste, krótkie zadania mogą być realizowane w funkcjach (korzystając z programowania proceduralnego) Skomplikowane funkcje powinny być rozdzielone na mniejsze Jeżeli funkcji jest zbyt dużo i obsługa danych zaczyna być problematyczna, wówczas trzeba zastanowić się nad zastosowaniem programowania obiektowego.
Tworzenie własnej klasy schemat postępowania 1. Analiza problemu: Co jest obiektem? Jakie są cechy obiektu istotne dla problemu? Co może się dziać z obiektem? 2. Projekt: Dane i stany, które należy przechowywać Operacje, które będą wykonywane na obiekcie 3. Implementacja tworzenie klas 4. Testowania tworzenie obiektów Atrybut1 Metoda1 Atrybut2 Metoda2 Jaka jest różnica między klasą, a obiektem? Slajd w oparciu o W. Dyrka, Wykład 3, Informatyka 2012
Tworzenie własnej klasy - przykład Problem: Pomiar stężenia hormonu w próbie badanych Obiekt: Wyniki pomiarów stężenia Jakie są atrybuty obiektu: Wektor zmierzonych wartości Nazwa Rozmiar próby Wartość średnia Odchylenie standardowe Co może się stać z obiektem: Wyświetlić dane o próbie Wykreślić histogram Można połączyć z inną grupą Porównać wartość średnią z inną grupą Slajd w oparciu o W. Dyrka, Wykład 3, Informatyka 2012
Projekt klasy Klasa ProbaLosowa Atrybuty: stezeniewektor -wektor zmierzonych stężeń nazwa - nazwa próby rozmiar - liczba badanych sr - wartość średnia z badań odch - odchylenie standardowe Metody: rysuj dodaj porównaj - wyświetla wartości - łączy próby - porównuje dwie próby
Definiowanie klasy classdef ProbaLosowa %Klasa oblsugujaca wyniki pomiarów stezenia hormonow w %probie wybranych pacjentow properties nazwa % nazwa proby stezeniewektor % wektor zmierzonych stezen rozmiar % liczba badanych sr % wartosc srednia z badan odch % odchylenie standardowe methods
Tworzenie obiektu obiekt jak struktura >> pr1=probalosowa pr1 = ProbaLosowa with properties: nazwa:[] stezeniewektor: [] rozmiar: [] sr: [] odch: [] >> pr1.stezeniewektor=10+2*randn(1,100); >> pr1.sr=10; >> pr1 pr1 = ProbaLosowa with properties: nazwa:[] stezeniewektor: [1x100 double] rozmiar: [] sr: 10 odch: [] >> pr1.stezenie=[10 20 30] No public field stezenie exists for class ProbaLosowa. >> class(pr1) ans = ProbaLosowa
Tworzenie obiektu - konstruktor Konstruktor to metoda, która służy do tworzenia obiektów danej klasy Nazwa konstruktora jest taka sama jak nazwa klasy Konstruktor powinien: Sprawdzać, czy argument wejściowy jest obiektem danej klasy, jeżeli tak to tworzyć jego kopię Jeśli nie, to powinien utworzyć obiekt z atrybutami podanymi jako argumenty wejściowe Konstruktor powinien sprawdzać poprawność wprowadzanych danych
Konstruktor - przykład function obj=probalosowa(wektor,nazwa) if (nargin==0) error('brak danych do utworzenia ProbyLosowej') elseif (isa(wektor,'probalosowa')) % kopiowanie obiektu obj=wektor; elseif (isvector(wektor)) if (sum(wektor<0))error('stezenie musi byc > 0'); if (nargin<2) nazwa='proba'; obj.stezeniewektor=wektor(:); else error('podano bledne dane') >> pr=probalosowa Error using ProbaLosowa (line 16) Brak danych do utworzenia ProbyLosowej >> pr=probalosowa([10,15,-1,14]) Error using ProbaLosowa (line 21) Stezenie musi byc > 0 >> pr=probalosowa([10,15;2,14]) Error using ProbaLosowa (line 25) Podano bledne dane
Enkapsulacja/hermetyzacja Oddzielenie i ukrycie wnętrzności klasy czyli implementacji (jak to działa?) Od powierzchowości klasy czyli interfejsu (jak z tego korzystać?) Ułatwia zachowanie poprawności obiektu ogranicza dostęp do danych Umożliwia ulepszenie sposobu działanie klasy bez zmiany sposobu korzystania z niej Podstawowa zasada programowania zorientowanego obiektowo Slajd W. Dyrka, Wykład 3, Informatyka 2012
Ustawienie wartości parametrów Ustawianie parametrów można kontrolować przez zdefiniowanie funkcji set (np. nazwa próby musi być łańcuchem znaków niezaczynającym się od liczby) function obj=set.nazwa(obj,nazwa) if (ischar(nazwa) && isempty(str2num(nazwa(1)))) obj.nazwa=nazwa; else error('zla nazwa!') >> pr.nazwa='1_pr' Error using ProbaLosowa/set.nazwa (line 36) Zla nazwa! >> pr=probalosowa([10,15,2,14],'2proba') Error using ProbaLosowa/set.nazwa (line 36) Zla nazwa! Error in ProbaLosowa (line 27) pr.nazwa=nazwa; Set działa również przy wywołaniu konstruktora!!
Pobieranie wartości parametrów dane zależne - liczba elementów, średnia, Klasa ProbaLosowa zawiera pola uzależnione od wektora zmierzonych wartości Rozmiar Średnia Odchylenie standardowe Nie trzeba ich trzymać w obiekcie można je obliczać. Tworzymy metody get, które będą je obliczać function rozmiar=get.rozmiar(obj) rozmiar=numel(obj.stezeniewektor) function sr=get.sr(obj) sr=mean(obj.stezeniewektor); function odch=get.odch(obj) odch=std(obj.stezeniewektor);
Liczba elementów, średnia, - dane zależne (2) Użytkownik nie powinien mieć możliwości ich nadpisywania. Tworzymy blok atrybutów zależnych (Depent) i edytowalnych tylko przez metody klasy (SetAccess=private): properties stezeniewektor % wektor zmierzonych stezen nazwa %nazwa proby properties (Depent=true,SetAccess=private) rozmiar % liczba badanych sr % wartosc srednia z badan odch % odchylenie standardowe
Kilka zasad Własności/atrybuty umieszczamy w blokach properties osobne bloki dla własności o różnych atrybutach Metody umieszczamy w blokach methods Konstruktor tworzy obiekt, nazywa się tak jak klasa function obj=probalosowa(wektor,nazwa) Funkcja get jest uruchamiana, gdy czytamy daną parametrem funkcji get jest obiekt, zwracana jest wartość własności function rozmiar=get.rozmiar(obj) Funkcja set jest uruchamiana gdy zmieniamy daną parametrami funkcji set są obiekt i nowa wartość, zwracany jest obiekt function obj=set.nazwa(obj,nazwa) Slajd W. Dyrka, Wykład 3, Informatyka 2012
Tworzenie własnej klasy co zostało? Problem: Pomiar stężenia hormonu w próbie badanych Obiekt: Wyniki pomiarów stężenia Jakie są atrybuty obiektu: Wektor zmierzonych wartości Nazwa Rozmiar próby Wartość średnia Odchylenie standardowe Co może się stać z obiektem: Wykreślić histogram Wyświetlić dane o próbie Można połączyć z inną grupą Porównać wartość średnią z inną grupą Slajd w oparciu o W. Dyrka, Wykład 3, Informatyka 2012
Rysowanie danych Chcemy wykreślić histogramy dla naszej próby Umieszczamy stosowną funkcję w bloku methods function rysuj(obj,n) %Rysuje histogram dla proby z podzialem na N koszykow. %Gdy N nie jest podane wylicza N wg. formuly Struga if (nargin==1) N = round(log2(obj.rozmiar)+1); hist(obj.stezeniewektor,n) tytul=['proba losowa ', obj.nazwa]; title(tytul) xlabel('stezenie') ylabel('n') %wywolanie >> pr.rysuj
Przeładowanie (przeciążanie) funkcji disp Do wyświetlania zmiennych służy funkcja disp >> disp(pr) ProbaLosowa with properties: stezeniewektor: [4x1 double] nazwa: 'pr1' rozmiar: 4 sr: 10.2500 odch: 5.9090 Chcemy wyświetlać obiekty naszej klasy w specjalny sposób. W bloku methods Definiujemy funkcję disp dla naszej klasy
Przeładowanie (przeciążanie) funkcji disp function disp(obj) %disp(proba) %Przeladowanie funkcji disp dla objektow klasy ProbaLosowa fprintf('parametry proby ''%s''\n Liczebnosc: %d\n Srednia: %.2f\n Odchylenie: %.2f\n',... obj.nazwa, obj.rozmiar, obj.sr, obj.odch) Działa przy każdym wyświetlaniu zmiennej >> disp(pr) Parametry proby 'proba1' Liczebnosc: 4 Srednia: 10.25 Odchylenie: 5.91 >> pr=probalosowa([10,15,2,14],'proba1') pr = Parametry proby 'proba1' Liczebnosc: 4 Srednia: 10.25 Odchylenie: 5.91
Przeładowanie operatorów dla klasy (1) Chcemy połączyć dwie badane próby za pomocą operatora + Operatory są związane z konkretnymi funkcjami http://www.mathworks.com/help/matlab/matlab_oop/implementing-operators-for-your-class.html
Przeładowanie operatorów dla klasy (2) Chcemy połączyć dwie badane próby za pomocą operatora + Operatory są związane z konkretnymi funkcjami Należy przeładować funkcję która nas interesuje (tutaj plus). function obj=plus(obj,obj2) %Przeladowanie operatora +, laczy wektory dwoch roznych prob %Nadaje nazwe nowej probie. obj.stezeniewektor=[obj.stezeniewektor;obj2.stezeniewektor]; nowanazwa=[obj.nazwa, '+', obj2.nazwa]; obj.nazwa=nowanazwa; >> pr=probalosowa(10+2*randn(1,100),'proba1'); >> pr2=probalosowa(5+0.5*randn(1,100),'proba2'); >> pr_polaczone=pr+pr2 pr_polaczone = Parametry proby 'proba1+proba2' Liczebnosc: 200 Srednia: 7.44 Odchylenie: 2.86
Przeładowanie operatorów dla klasy (3) Chcemy porównać średnie dwóch prób. Przeładujmy operator ==, czyli funkcję eq function [flag,h,p,ci,stats]=eq(obj,obj2) %Funkcja testuje hipoteze o rownosci srednich %Wykorzystuje test t-studenta, alpha = 0.05 [h,p,ci,stats]=ttest(obj.stezeniewektor,obj2.stezeniewektor); flag=not(h); >> pr == pr2 ans = 0 >>[flag,h,p,ci,stats]=pr==pr2; % dostajemy wszystkie parametry wyjsciowe
Programowanie zorientowane obiektowo - podsumowanie Poznanie obiektów już istniejących: class(obiekt), properties(obiekt), methods(obiekt) Tworzenie obiektów to proces: (1) analiza problemu, (2) projekt klas, (3) implementacja (4) testowanie. Enkapsulacja rozgraniczenie między działaniem, a korzystaniem z klasy Przeładowanie (przeciążanie) funkcji definiowanie istniejących funkcji, aby działały w sposób szczególny dla nowej klasy
Plan wykładów - podsumowanie 1. Wprowadzenie (historia, środowisko programistyczne, podstawowe komy) 2. Skrypty. Instrukcje sterujące. Budowanie funkcji. 3. Optymalizacja obliczeń. Przetwarzanie tekstu. Wizualizacja danych. Praca z plikami. Strukturyzacja danych. 4. Graficzny interfejs użytkownika. 5. Interpolacja oraz obliczenia numeryczne. 6. Rekurencja. Programowanie obiektowe. 7. Kolokwium termin I. 09.01.2018 8. Kolokwium termin II. 23.01.2018
Co zostało do zrobienia. 1. Przygotować ciekawe projekty. 2. Zaliczyć zajęcia laboratoryjne. 3. Nauczyć się do kolokwium. 4. Zaliczyć wykład. 5. Wypełnić ankiety po zakończeniu kursu (wykład i laboratorium).