Paradygmaty Programowania Wykład 1 Wiadomości wstępne Nieliniowej, WMiI UŁ 1
Czym jest paradygmat? Paradygmat to przyjęty sposób widzenia rzeczywistości w danej dziedzinie, doktrynie itp. Zespół form fleksyjnych (deklinacyjnych lub koniugacyjnych) właściwy danemu typowi wyrazów. źródło: słownik języka polskiego, PWN Nieliniowej, WMiI UŁ 2
Czym jest paradygmat programowania? Paradygmat programowania (z ang. programming paradigm), to wzorzec programowania przedkładany w danym okresie rozwoju informatyki ponad inne lub szczególnie ceniony w pewnych okolicznościach lub zastosowaniach. Paradygmat programowania definiuje sposób patrzenia programisty na przepływ sterowania i wykonywanie programu komputerowego. źródło: www.wikipedia.pl Nieliniowej, WMiI UŁ 3
Podział paradygmatów Programowanie Imperatywne Deklaratywne Strukturalne Proceduralne Logiczne Funkcyjne Obiektowe Generyczne SQL, XML Współbieżne Zdarzeniowe Nieliniowej, WMiI UŁ 4
Skąd taki podział? Programowanie imperatywne to paradygmat programowania, który opisuje proces wykonywania jako sekwencję instrukcji zmieniających stan programu. Podobnie jak tryb rozkazujący w lingwistyce wyraża żądania jakichś czynności do wykonania, programy imperatywne składają się z ciągu komend do wykonania przez komputer. Programowanie deklaratywne to paradygmat programowania, w którym programista zamiast definiowania sposobu rozwiązania, czyli sekwencji kroków prowadzących do uzyskania wyniku, opisuje samo rozwiązanie. Innymi słowy programowanie w tych językach polega na opisywaniu tego co nas interesuje a nie jak to zrobić (brak opisu przepływu sterowania). Nieliniowej, WMiI UŁ 5
Jak patrzeć na programowanie? Czym jest program komputerowy? program = kod + dane kod = algorytm + język programowania dane = struktury danych + język programowania Interesujący może być teraz: sposób patrzenia na kod sposób patrzenia na dane sposób wiązania danych z kodem sposób przepływu sterowania Nieliniowej, WMiI UŁ 6
Cztery główne paradygmaty Paradygmat programowania Proceduralnego/Strukturalnego Paradygmat programowania Obiektowego Paradygmat programowania Funkcyjnego Paradygmat programowania Logicznego Nieliniowej, WMiI UŁ 7
Programowanie Proceduralne/Strukturalne Paradygmat programowania proceduralnego (z ang. procedural programming), to paradygmat programowania zalecający dzielenie kodu na procedury, czyli fragmenty wykonujące ściśle określone operacje. Procedury nie powinny korzystać ze zmiennych globalnych (w miarę możliwości), lecz pobierać i przekazywać wszystkie dane (czy też wskaźniki do nich) jako parametry wywołania. Paradygmat programowania strukturalnego (z ang. structured programming), to paradygmat zalecający hierarchiczne dzielenie kodu na bloki, z jednym punktem wejścia i jednym lub wieloma punktami wyjścia. Chodzi przede wszystkim o nieużywanie (lub ograniczenie) instrukcji skoku (goto). Trzema kluczowy strukturami są: - sekwencja (instrukcja_1; instrukcja_2; ; instrukcja_n), - wybór (if, if...else, switch, case), - powtarzanie (while, repeat, for). Strukturalność zakłócają instrukcje typu: break, continue, które jednak w niektórych przypadkach znacząco podnoszą czytelność kodu i elastyczność kodu. Kluczowe koncepcje: zmienne, typy, procedury i dane abstrakcyjne. Zastosowania: oprogramowanie sieciowe, systemy operacyjne. Języki charakterystyczne dla tych paradygmatów, to m.in.: Fortran (lata 50. XX wieku), Cobol (lata 60. XX wieku), Pascal (lata 70. XX wieku), C (lata 70. XX wieku), C++ (lata 80. XX wieku), i inne. Nieliniowej, WMiI UŁ 8
Programowanie Proceduralne/Strukturalne - przykład Nieliniowej, WMiI UŁ 9
Programowanie Obiektowe Paradygmat programowania obiektowego (ang. object-oriented programming), to paradygmat programowania, w którym programy definiuje się za pomocą obiektów - elementów łączących stan (czyli dane, nazywane najczęściej polami) i zachowanie (czyli procedury, tu: metody). Obiektowy program komputerowy wyrażony jest jako zbiór takich obiektów, komunikujących się pomiędzy sobą w celu wykonywania zadań. Kluczowe koncepcje: klasy i obiekty, dziedziczenie, enkapsulacja, polimorfizm. Zastosowania: aplikacje WWW i aplikacje desktopowe współczesny rynek oprogramowania. Języki charakterystyczne dla tego typu paradygmatu, to m.in.: Simula (lata 60. XX wieku), Smalltalk (lata 80. XX wieku), C++ (lata 90. XX wieku), C# (początek XXI wieku), Java (lata 90. XX wieku), i inne. Nieliniowej, WMiI UŁ 10
Programowanie Obiektowe - przykład Nieliniowej, WMiI UŁ 11
Programowanie Funkcyjne Paradygmat programowania funkcyjnego lub programowania funkcjonalnego (z ang. functional programming), to paradygmat programowanie będący odmianą programowania deklaratywnego, w której funkcje należą do wartości podstawowych, a nacisk kładzie się na wartościowanie (często rekurencyjnych) funkcji, a nie na wykonywanie poleceń. Podstawą teoretyczną programowania funkcyjnego był opracowany w latach 30. XX wieku przez Alonzo Churcha rachunek lambda, a dokładnie rachunek lambda z typami. Kluczowe koncepcje: funkcje, rachunek lambda, parametryczny polimorfizm. Zastosowania: teoretyczne, telekomunikacja, obliczenia finansowe. Języki charakterystyczne dla tego typu paradygmatu, to m.in.: Lisp (lata 50. XX wieku), ML (lata 70. XX wieku), Haskell (lata 80. XX wieku), H# (lata 80. XX wieku), Erlang (lata 80. XX wieku) i inne. Nieliniowej, WMiI UŁ 12
Programowanie Funkcyjne - przykład Nieliniowej, WMiI UŁ 13
Programowanie Logiczne Paradygmat programowania logicznego nazywanego także programowaniem w logice lub programowaniem w języku logiki (z ang. logic programming), to będąca odmianą programowania deklaratywnego metoda programowania, w której program podawany jest jako pewien zestaw zależności, i relacji zachodzących między tymi zależnościami. Kluczowe koncepcje: fakty, relacje i zapytania. Zastosowania: teoretyczne, sztuczna inteligencja (przetwarzanie języka naturalnego, rozpoznawanie obrazów). Języki charakterystyczne dla tego typu paradygmatu, to m.in.: Gödel (lata 70. XX wieku), Fril (lata 70. XX wieku), Prolog (lata 70. XX wieku), i inne. Nieliniowej, WMiI UŁ 14
Programowanie Logiczne - przykład Nieliniowej, WMiI UŁ 15
Popularność języków a cztery główne paradygmaty źródło: www.tiobe.com Nieliniowej, WMiI UŁ 16
Programowanie zdarzeniowe (imperatywne) Jest to schemat programowania w którym napisany przez nas program komputerowy (aplikacja) musi cały czas reagować (odpowiadać) na zdarzenia (events) wywoływane przez użytkownika, na ogół są to: kliknięcia myszy, klawiatury, odświeżania okienek i inne. Ten typ programowania jest głównie stosowany przy budowaniu GUI. Przykładem może być oprogramowanie aplikacji graficznych w Javie pisanych z wykorzystaniem bliblioteki Swing i implementacja interjesów nasłuchu typu: SomethingListener Nieliniowej, WMiI UŁ 17
Programowanie generyczne (imperatywne) Idea programowania oparta o założenie niepowtarzalności pewnych fragmentów kodu programu. Dla przykładu możemy zadeklarować pewien wzorzec klasy implementującej działanie listy. Oczywiście do opisu działania listy nie ma potrzeby znajomość typów które będą w niej przechowywane, np. pobieranie następnego elementu listy, czy usunięcie elementu z listy. Można zatem przeprowadzić opis wzorca listy dla dowolnego typu T i następnie podstawiać tylko podczas działania programu pod T konkretne typy, np.: float czy string. Wiele nowoczesnych języków programowania: Java, C# (typy uogólnione), C++ (szablony) i inne ma zaimplementowany mechanizm programowania uogólnionego. Nieliniowej, WMiI UŁ 18
Programowanie współbieżne (imperatywne) Jest to paradygmat programowania polegający na wykonywaniu wielu zadań (na ogół obliczeń) w tym samym czasie (równocześnie). Jest to typ programowania związany z wątkami oraz z pracą na nich (ich koordynacją). Zadaniami należy rozsądnie zarządzać aby nie dopuścić do blokady programu. Blisko programowania współbieżnego znajduje się programowanie równoległe i rozproszone. Programowanie równoległe polega na jednoczesnym wykonywaniu zadań przez wiele procesorów. Programowanie rozproszone polega na wykonywaniu zadań przez wiele komputerów (procesorów) połączonych siecią. Język Java dostarcza prostych mechanizmów pracy na wątkach, tj. daje możliwość programowania współbieżnego. W C++ można korzystać z zewnętrznych bibliotek umożliwiających pracę na wątkach. Nieliniowej, WMiI UŁ 19
SQL strukturalny język zapytań używany do tworzenia, modyfikowania baz danych oraz do umieszczania i pobierania danych z baz danych: SELECT * FROM pracownicy WHERE pensja > 2000 ORDER BY staz DESC; SQL, XSLT (deklaratywne) XSLT język przekształceń dokumentów XML pozwalający tłumaczyć dokumenty z jednego formatu XML na dowolny inny <xsl:for-each select="sprawozdanie/dane/pozycja"> <tr> </tr> <td style="background-color:yellow"> </td> <xsl:value-of select="miesiac" /> <td style="background-color:olive"> </td> </xsl:for-each> <xsl:value-of select="wartosc" /> Nieliniowej, WMiI UŁ 20
Pytanie o język programowania? Wszystko co dalej powiemy na wykładzie dotyczyć będzie języków programowania. Warto zatem zadać pytania: Czym jest język programowania? Jak go opisać? Jak interpretować ten zapis? Spróbujemy teraz krótko odpowiedzieć na te pytania. Nieliniowej, WMiI UŁ 21
Definicja języka Niech Σ oznacza dowolny skończony i niepusty zbiór (elementy tego zbioru nazywamy symbolami, zaś sam zbiór alfabetem). Niech Σ* oznacza zbiór wszystkich skończonych ciągów elementów z Σ (jest to zbiór wszystkich słów nad alfabetem Σ). Językiem L nazywamy dowolny podzbiór zbioru Σ*, tj. L Σ*. Jeśli dla przykładu przyjmiemy, że Σ = {a,b,c}, to przykładowe języki mogą mieć postać: L=Ø, L={Λ,a,b,c}, L={ab,ba,ac,ca,bc,cb,aa,bb,cc}, L={ab,aab,aaab,aaaab, }, L=Σ*, (Λ oznacza słowo puste), są poprawnymi językami w świetle powyższej definicji. Oczywiście nie wszystkie języki są warte uwagi. Nieliniowej, WMiI UŁ 22
Język programowania Jeżeli za Σ przyjmiemy zbiór znaków ASCII, to języki programowania będą specjalnymi podzbiorami Σ*. Podzbiory te są nieskończone i nie da się ich opisać poprzez wypisanie wszystkich ich elementów. Potrzebny jest inny sposób i takim sposobem mogą być gramatyki, które ogólnie rzecz ujmując zajmują się badaniem reguł, które rządzą generowaniem słów i zdań języka. Interesujące z punktu widzenia języków programowania są dwie gramatyki: regularne i bezkontekstowe. Gramatyka regularna, to gramatyka formalna generująca język regularny, czyli dający się wytworzyć za pomocą Deterministycznego Automatu Skończonego. Ten typ gramatyk nadaje się do opisu jednostek leksykalnych języka, czyli liczb i identyfikatorów. Gramatyka bezkontekstowa, to gramatyka formalna generująca język bezkontekstowy, czyli dający się wytworzyć za pomocą Automatu ze Stosem. Ten typ gramatyk nadaje się do opisu składni języka: instrukcji, deklaracji, czy też całego programu. Nieliniowej, WMiI UŁ 23
Składnia i semantyka języka Składnia (z ang. syntax) jest to zbiór reguł, mówiących jak wygląda poprawnie zbudowany program w danym języku, a dokładniej: jak tworzymy polecenia i wyrażenia, jaką postać mają struktury sterowania, jak zapisuje się deklaracje. Np. a if wyrazenie1 then instrukcja1 // w Pascal if (wyrazenie1) instrukcja1 // w C++ Semantyka (z ang. semantics) to znaczenie konstrukcji w danym języku, czyli zrozumienie co te konstrukcje naprawdę robią. Do formalnego opisu semantyki języka służą: semantyki operacyjne, semantyki aksjomatyczne, semantyki denotacyjne, oraz semantyki logiczne. Dla przykładu znaczenie powyższej instrukcji jest następujące: jeśli wyrażenie1 jest prawdziwe, to wykonaj instrukcja1. Nieliniowej, WMiI UŁ 24
Jak w praktyce opisać język programowania? Składnię języka możemy opisać stosując notację BNF (z ang. Backus Naur form), czyli stosując sposób zapisu reguł gramatyki bezkontekstowej. Notacja ta jest powszechnie stosowana w informatyce do zapisu składni języków programowania. Została wymyślona przez Johna Backusa w latach 50. w czasie prac nad językiem Fortran, a następnie zmodyfikowana została przez Petera Naura i użyta do zdefiniowania składni języka Algol. Notacja BNF, to zbiór reguł postaci: Reguły posługiwania się ta notacją: <symbol>::=<definicja_symbolu> 1. Pod symbol możemy podstawić jego definicję, 2. Symbole z lewej strony zwane są symbolami nieterminalnymi, a symbole z prawej strony, to symbole terminalne. 3. Symbole terminalne pochodzą z alfabetu danego języka, a nieterminalne są symbolami pomocniczymi. Semantykę języka zazwyczaj opisujemy w języku naturalnym, czyli w naszym ojczystym. Nieliniowej, WMiI UŁ 25
Przykład notacji BNF Liczbę naturalną ze zbioru {0,1,2, } w notacji BNF moglibyśmy zdefiniować w następujący sposób: 1. <zero>::= 0 Przykład wartości: 0 2. <cyfra niezerowa>::= 1 2 3 4 5 6 7 8 9 Przykład wartości: 1, 2, 3 3. <cyfra>::= <zero> <cyfra niezerowa> Przykład wartości: 0, 1, 2, 3 4. <ciąg cyfr> ::= <cyfra> <cyfra><ciąg cyfr> Przykład wartości: 0, 1, 01, 23, 45, 99, 10023, 000001 5. <liczba naturalna>::= <cyfra> <cyfra niezerowa><ciąg cyfr> Przykład wartości: 0, 1, 2, 34 56, 406, 556066 Listę argumentów w notacji BNF moglibyśmy zdefiniować w następujący sposób: 1. <lista_arg>::= arg {, arg} Instrukcję warunkową if else w notacji BNF moglibyśmy zdefiniować w następujący sposób: 1. <if-stmt>::=if <exp> <statement> else <statement> 2. <statement>::=<if-stmt> other 3. <expr>::=0 1 Nieliniowej, WMiI UŁ 26
Kompilator a Interpreter Kompilator (z ang. compiler) to program służący do automatycznego tłumaczenia kodu napisanego w jednym języku (języku źródłowym) na równoważny kod w innym języku (języku wynikowym). Proces ten nazywany jest kompilacją. W informatyce pojęciem kompilatora określa się najczęściej program do tłumaczenia kodu źródłowego w języku programowania na język maszynowy. Niektóre z nich tłumaczą najpierw do języka asemblera, a ten na język maszynowy jest tłumaczony przez asembler. Interpreter (z ang. interpreter) to program komputerowy, który analizuje kod źródłowy programu, a przeanalizowane fragmenty wykonuje. Dzieje się tak inaczej niż w procesie kompilacji, podczas którego nie wykonuje się wejściowego programu (kodu źródłowego), lecz tłumaczy go do wykonywalnego kodu maszynowego lub kodu pośredniego, który jest następnie zapisywany do pliku w celu późniejszego wykonania. Nieliniowej, WMiI UŁ 27
Dziękuję za Uwagę!!! Nieliniowej, WMiI UŁ 28