Laboratorium przedmiotu „Paradygmaty Programowania”
Transkrypt
Laboratorium przedmiotu „Paradygmaty Programowania”
Laboratorium przedmiotu „Paradygmaty Programowania” Laboratorium 6 Podstawy programowania w języku Scheme i środowisko DrScheme. 1. Wprowadzenie Język Scheme należy do rodziny funkcyjnych języków programowania. Został stworzony w latach 70 jako uproszczenie i ulepszenia języka Lisp. Jako język programowania ogólnego przeznaczenia, Scheme pozwala na tworzenie bardzo szerokiego spektrum aplikacji. DrScheme jest środowiskiem programistycznym umożliwiającym tworzenie aplikacji w wielu odmianach języka Scheme. Środowisko to można bezpłatnie pobrać ze strony internetowej projektu http://www.plt-scheme.org/. Informacje dotyczące tej części przedmiotu można też odnaleźć w następującej literaturze: • • • M. Felleisen, R. B. Findler, M. Flatt, S. Krishnamurthi „Projektowanie oprogramowania. Wstęp do programowania i techniki komputerowej“, Helion, 2003. M. Felleisen, R. B. Findler, M. Flatt, S. Krishnamurthi „How to Design Programs. An Introduction to Computing and Programming”, online: http://www.htdp.org/2003-0926/Book/. (oryginalna wersja poprzedniej pozycji). R. Kent Dybvig, „The Scheme Programming Language. Third Edition”, online: http://www.scheme.com/tspl3/. 2. Środowisko DrScheme 2. 1 Rozpoczęcie pracy z środowiskiem DrScheme. Po uruchomieniu środowiska pojawia się okno główne programu (przedstawione na rys. 1), które składa się z czterech głównych elementów: paska narzędziowego, okna definicji, okna interakcji oraz paska statusu. Paradygmaty programowania 2 Rysunek 1 Okno główne programu DrScheme Środowisko DrScheme obsługuje wiele wersji języka Scheme różniące się stopniem zaawansowania użytkownika. W celu wybrania odpowiedniej – dla danych zajęć – wersji języka, należy z menu „Language” wybrać opcję „Choose language” co spowoduje wyświetlenie się następującego okna dialogowego: Rysunek 2 Okno wyboru języka Paradygmaty programowania 3 Na początku każdych zajęć należy wybrać odpowiednią wersję języka podaną w instrukcji. UWAGA!! W przypadku pierwszego uruchomienia środowiska wybranie języka, z którego chce się korzystać jest obligatoryjne. 2.1.1 Okno definicji Okno definicji służy jako edytor do definiowania programu w języku Scheme. Na rysunku 1 przedstawiony został przykładowy program służący do obliczania wartości funkcji silnia1: Przykład 1 – przykładowa funkcja w języku Scheme: ;;funkcja silnia: liczba->liczba (define (silnia n) (if (zero? n) 1 (* n (silnia (- n 1))))) W języku Scheme nawiasy są wszechobecne w bardzo dużej liczbie, dlatego programowanie wymaga szczególnej uwagi. Na szczęście edytor DrScheme posiada funkcje zarządzania nawiasami. Przede wszystkim bardzo przydatną funkcją jest zmiana koloru tła pomiędzy odpowiadającymi sobie nawiasami w przypadku gdy kursor znajduje się koło jednego z nich. Rysunek 3 Przykładowy program w oknie definicji W przypadku przejścia do nowej linii edytor dokonuje automatycznych wcięć w kodzie programu. Należy pamiętać jednak, że funkcja ta ma charakter czysto estetyczny, gdyż podobnie jak w innych popularnych językach programowania tj. C++, Pascal czy Java również w przypadku Scheme białe znaki nie mają znaczenia. 2.1.2 Okno interakcji Okno interakcji pozwala na wprowadzanie wyrażeń języka Scheme w celu ich natychmiastowego wykonania. Po wprowadzeniu i zatwierdzeniu wyrażenia DrScheme 1 Program ten przedstawia również sposób zapisywania komentarzy, które w języku Scheme poprzedza się podwójnym znakiem ‘;’. Paradygmaty programowania 4 wykona je i wyświetli wynik lub komunikat o błędzie. Następnie w oknie interakcji pojawi się kolejny znak zachęty „>” i system będzie oczekiwał na kolejne wyrażenie. W przypadku gdy wprowadzone wyrażenie nie jest kompletne, lecz użytkownik naciśnie Enter DrScheme rozpocznie nową linię i będzie oczekiwał na wprowadzenie dalszej części wyrażenia. Poprzednio wprowadzone wyrażenie można skopiować do bieżącej linii wciskając kolejno ESC i p. 2.2 Analizator składni Ciekawą opcją środowiska jest analizator składni. Przedstawia ona – w sposób graficzny – relacje pomiędzy funkcjami i zmiennymi w programie wpisanym w oknie definicji. Podstawowym mechanizmem oferowanym przez analizator składni jest jej kolorowanie. Ponadto najeżdżając kursorem myszy na jakikolwiek symbol wprowadzony przez użytkownika pokazuje miejsce, w którym ten symbol został zdefiniowany. Z kolei, gdy użytkownik najedzie kursorem myszy na definicję symbolu (jego pierwsze wystąpienie w programie) analizator pokaże wszystkie jego wystąpienia w programie (rys. 3) Rysunek 4 Okno Analizatora składni Dodatkowo klikając prawym przyciskiem myszy nad jednym z symboli, wyświetla się menu kontekstowe: Paradygmaty programowania 5 Rysunek 5 Menu kontekstowe w analizatorze składni Ciekawą opcją jest „Rename …” pozwalającą na zmianę nazwy symbolu w całym wyrażeniu. 2.3 Okno Debug Kolejną opcją oferowaną przez środowisko DrScheme jest debugowanie programu, pozwalające na wykonywanie jego kodu krok po kroku. Okno debug, przedstawione na rys.4 otwiera się klikając przycisk: Rysunek 6 Okno debugowania Podzielone jest ono na 4 okna: definicji, interakcji stosu i zmiennych. Okno definicji w tym przypadku jest oknem tylko do odczytu. Zielona strzałka jaka się w nim pojawia przedstawia obecny punkt wykonania programu. Dodatkowo pozwala ono na ustawianie tzw. „breakpoint -ów” , czyli punktów, w których ma się zatrzymać wykonywanie programu. Breakpointy ustawiane są przez kliknięcie na kodzie programu prawym przyciskiem myszy i wybranie z menu kontekstowego opcji „Pause at this point”. Okna stosu i zmiennych służą do przedstawiania odpowiednio: jakie instrukcje obecnie znajdują się na stosie oraz jaka jest obecna wartość zmiennych. Przyciski jakie pojawiły się na pasku znajdującym się nad oknem definicji oznaczają odpowiednio: Paradygmaty programowania 6 Wstrzymanie wykonywania polecenia Wykonanie programu do końca Obliczenie wartości następnego wyrażenia w programie Obliczenie następnego wyrażenia bez zagłębiania się w wyrażenia wewnętrzne Wykonanie obecnej funkcji do końca UWAGA!!! Opcja debugowania programu nie jest dostępna w następujących wersjach języka: How to Design Programs Beginning Student Beginning Student with List Abbreviations Itermediate Student Intermediate Student with lambda 2.4 Stepper Kolejną ciekawą cechą środowiska DrScheme jest Stepper. Pozwala on na wizualną analizę procesu obliczania wartości wyrażenia. Rysunek 7 Okno steppera w środowisku DrScheme Paradygmaty programowania 7 UWAGA!!! Opcja Steppera jest dostępna tylko w następujących wersjach języka: How to Design Programs Beginning Student Beginning Student with List Abbreviations Itermediate Student Intermediate Student with lambda 3. Podstawy programowania w języku Scheme 3.1 Notacja prefixowa W większości języków programowania wyrażenia zapisuje się zgodnie z notacją infixową, w której operator występuje pomiędzy operandami np. 3+4, (23.5 – 7) / 4. Język Scheme wykorzystuje nawiast oraz notację prefixową, w której operator występuje przed operandami, czyli: > (+ 3 4) 7 > (- (- 23.5 7) 4) 4.125 3.2 System typów języka Scheme 3.2.1 Typy numeryczne • • • • Typ całkowity: 3, 0, -12 Typ ułamkowy: 1/3, -4/5 Typ zmiennoprzecinkowy: 0.3, -3.14 Typ liczb złożonych: 0.0+1.0i oznacza √−1 W języku Scheme typy numeryczne nie mają określonego z góry zakresu wartości, gdyż język ten wykorzystuje tzw. arytmetykę dowolnej precyzji (arbitrary arithmetic). W praktyce oznacza to, że dokładność obliczeń ograniczona jest tylko rozmiarem posiadanej pamięci. Przykład 2 – wywołanie funkcji silnia zdefiniowanej w przykładzie 1: > (silnia 1000) 4023872600770937735437024339230039857193748642107146325437999104299385123986290205920 4420848696940480047998861019719605863166687299480855890132382966994459099742450408707 3759918823627727188732519779505950995276120874975462497043601418278094646496291056393 8874378864873371191810458257836478499770124766328898359557354325131853239584630755574 0911426241747434934755342864657661166779739666882029120737914385371958824980812686783 8374559731746136085379534524221586593201928090878297308431392844403281231558611036976 8013573042161687476096758713483120254785893207671691324484262361314125087802080002616 8315102734182797770478463586817016436502415369139828126481021309276124489635992870511 4964975419909342221566832572080821333186116811553615836546984046708975602900950537616 4758477284218896796462449451607653534081989013854424879849599533191017233555566021394 5039973628075013783761530712776192684903435262520001588853514733161170210396817592151 Paradygmaty programowania 8 0907788019393178114194545257223865541461062892187960223838971476088506276862967146674 6975629112340824392081601537808898939645182632436716167621791689097799119037540312746 2228998800519544441428201218736174599264295658174662830295557029902432415318161721046 5832036786906117260158783520751516284225540265170483304226143974286933061690897968482 5901254583271682264580665267699586526822728070757813918581788896522081643483448259932 6604336766017699961283186078838615027946595513115655203609398818061213855860030143569 4527224206344631797460594682573103790084024432438465657245014402821885252470935190620 9290231364932734975655139587205596542287497740114133469627154228458623773875382304838 6568897646192738381490014076731044664025989949022222176590433990188601856652648506179 9702356193897017860040811889729918311021171229845901641921068884387121855646124960798 7229085192968193723886426148396573822911231250241866493531439701374285319266498753372 1894069428143411852015801412334482801505139969429015348307764456909907315243327828826 9864602789864321139083506217095002597389863554277196742822248757586765752344220207573 6305694988250879689281627538488633969099598262809561214509948717012445164612603790293 0912088908694202851064018215439945715680594187274899809425474217358240106367740459574 1785160829230135358081840096996372524230560855903700624271243416909004153690105933983 8357779394109700277534720000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000 3.2.2 Typ logiczny Typ logiczny przyjmuje w języku Scheme dwie wartości: true lub false (w niektórych implementacjach tego języka oznaczanych również symbolami, odpowiednio #t i #f. Przykład 3 – typ logiczny: >(integer? 4) true >(integer? 3.5) false >(integer? 1/3) false >(not true) false 3.2.3 Typ znakowy Dane znakowe w języku Scheme są reprezentowane poprzez dołączenie do znaku prefixu #\, czyli: #\a oznacza znak a. Pewne znaki mają przypisane bardziej opisowe nazwy np. #\newline, #\tab, #\space. Przykład 4 – typ znakowy: >(char? #\space) true >(char-upcase #\a) #\A >(char-downcase #\D) #\d Paradygmaty programowania 9 3.2.4 Łańcuchy znaków Łańcuchy znaków definiuje się w języku Scheme jako sekwencję znaków umieszczoną pomiędzy cudzysłowami. Łańcuchy znaków są w języku Scheme typem złożonym. Przykład 5 – Łańcuchy znaków: >”Scheme jest fajny” ”Scheme jest fajny” >(string-append ”Scheme” ” jest” ” fajny”) ”Scheme jest fajny” 3.2.5 Symbole Symbole służą do przedstawiania informacji symbolicznej. Ich zadaniem jest reprezentowanie prostych danych jak imiona, polecenia, nagłówki. W przeciwieństwie do łańcuchów znaków symbole są typem prostym. W języku Scheme symbole są ciągami znaków poprzedzonymi pojedynczym apostrofem, lub funkcją quote (w symbolach nie można stosować znaków spacji i przecinków). Przykład 6 – Symbole >’Witaj ’Witaj >(quote Witaj) ’Witaj 3.3 Definiowanie funkcji w języku Scheme Definiowanie funkcji w języku Scheme jest wyjątkowo proste. Wykorzystywane jest w tym celu słowo kluczowe define, po którym występuje deklaracja funkcji (w nawiasach występują nazwa funkcji, wymagane parametry) oraz jej ciało: (define (nazwa_funkcji parametry) (wyrazenie)) Przykład 7 – Definiowanie funkcji: (define (razy2 x) (* 2 x) (define (dodaj x y) (+ x y)) wywołanie funkcji (w oknie interakcji): >(razy2 4) 8 >(dodaj 2 3) 5 Paradygmaty programowania 10 W środowisku DrScheme funkcje definiowane są w oknie definicji opisanym w punkcie 2.1.1. Aby możliwe było wykorzystanie zdefiniowanej wcześniej funkcji, środowisko DrScheme wymaga kliknięcia przycisku na pasku narzędzi okna głównego. W przypadku, gdy użytkownik chce wywołać funkcję bez uprzedniego kliknięcia na ten przycisk wyświetlony zostanie komunikat: 3.4 Instrukcje warunkowe 3.4.1 Wyrażenie warunkowe Warunkami będziemy nazywać wszystkie wyrażenia, które w wyniku zwracają wartość typu logicznego. Język Scheme posiada wbudowanych szereg funkcji, które mogą być wykorzystane jako warunki np. number?, string?, char?, odd?, even?, zero?, not. Zaimplementowane w nim zostały również podstawowe operatory logiczne tj. =, <, >, <=, >=. Dodatkowo wyrażenia warunkowe mogą być łączone za pomocą funkcji and i or: Przykład 8 – Wyrażenia warunkowe (define (CzyLiczbaJestWiekszaNiz10 x) (and (number? x) (> x 10))) >(CzyLiczbaJestWiekszaNiz10 3) false >(CzyLiczbaJestWiekszaNiz10 12) true 3.4.2 Funkcja warunkowa if Funkcja warunkowa if jest odpowiednikiem dobrze znanej, z innych języków programowania, instrukcji warunkowej. Składnia jej przedstawia się następująco: Paradygmaty programowania 11 (if wyrażenie_warunkowe wyrażenie_prawda wyrażenie_fałsz) Wyrażenie_prawda jest obliczane w przypadku gdy wyrażenie_warunkowe zwróci wartość true, w przeciwnym przypadku obliczane jest wyrażenie wyrażenie_fałsz. Przykład 9 – Funkcja warunkowa if: (define (powiekszDodatnieO2aUjemneO5 x) (if (> x 0) (+ x 2) (+ x 5))) >(powiekszDodatnieO2aUjemneO5 4) 9 >(powiekszDodatnieO2aUjemneO5 -4) 1 3.4.3 Funkcja warunkowa cond Funkcja warunkowa cond upraszcza tworzenie zagnieżdżonych funkcji if. Składnia tej funkcji przedstawia się następująco: (cond (wyrażenie_warunkowe1 wyrażenie1) (wyrażenie_warunkowe2 wyrażenie2) (wyrażenie_warunkowe3 wyrażenie3) ... (wyrażenie_warunkoweN wyrażenieN) (else wyrażenie)) Wystąpienie funkcji else jest opcjonalne. Przykład 10 – Funkcja warunkowa cond: (define (sprawdz x) (cond ((< x 0) ”liczba mniejsza od 0”) ((and (>= x 0) (<= x 10)) ”liczba z przedziału od 0 do 10”) ((> x 10) ”liczba większa od 10”))) >(sprawdz -1) ”liczba mniejsza od 0” >(sprawdz 5) ”liczba z przedziału od 0 do 10” >(sprawdz 12) ”liczba większa od 10” Paradygmaty programowania 12 3.5 Deklaracje zmiennych Choć w językach czysto funkcyjnych zmienne, znane z języków imperatywnych nie istnieją, w literaturze pojęcie to często stosowane jest w celu określenia etykiety dla wartości, której po zdefiniowaniu nie można modyfikować2. W języku Scheme takie wartości definiujemy – podobnie jak funkcje – za pomocą polecenia define, lecz w tym przypadku składnia jest troszkę inna: (define nazwa wyrazenie) Przykład 11 – Deklarowanie zmiennych za pomocą polecenia define (define DLUGOSC_BOKU 20) (define POLE_POW (* DLUGOSC_BOKU DLUGOSC_BOKU)) Lokalne zmienne można też definiować wewnątrz funkcji za pomocą polecenia let. (let ((nazwa1 wyrazenie1) (nazwa2 wyrazenie2) … (nazwaN wyrazenieN)) (wyrazenie_ciala_funkcji)) Zmienne te są widoczne wyłącznie w wyrażeniu stanowiącym ciało polecenia let. Przykład 12 – Deklarowanie zmiennych za pomocą polecenia let (define (sumuj) (let ((x 1) (y 2)) (+ x y))) polecenia let mogą być zagnieżdżone: (define (sumuj) (let ((x 1)) (let ((y 2)) (+ x y)))) 2 Język Scheme nie jest językiem czysto funkcyjnym, dlatego zaimplementowana jest w nim konstrukcja pozwalająca na modyfikację zdefiniowanych wartości, nie będziemy się nią jednak zajmować na zajęciach. Paradygmaty programowania 13 4. Zadania do samodzielnego wykonania 1. W poniższych wyrażeniach uzupełnij nawiasy tak, aby były to poprawne wyrażenia języka Scheme: a. / / * * 1 2 3 4 5 6 b. * 1 – 2 * 3 4 + / 5 6 7 c. + * 1 2 - + * 3 4 / 5 6 Uwaga: może istnieć więcej niż jedno rozwiązanie 2. Przekształć poniższe wyrażenia z zapisu matematycznego lub infiksowego na zapis prefiksowy a. 33 + ∗ (23 − () b. (7*2 / 28) * (2/3) c. 10) + (1 − 2) ∗ 15 Uwaga: do potęgowania służy funkcja expt 3. Funkcje trygonometryczne zaimplementowane w Scheme (sin, cos, tan) przyjmują argument, który określa wartość kąta wyrażoną w radianach. Napisz funkcje zamieniającą stopnie na radiany, a następnie nowe wersje funkcji trygonometrycznych (sinS, cosS, tanS), w których argument określa wartość kąta wyrażoną w stopniach. Uwaga: W funkcji zamieniającej stopnie na radiany należy zadeklarować zmienną pi i przypisać jej wartość 3.14 4. Zdefiniować funkcję silnia z przykładu 1 i za jej pomocą obliczyć silnię z 2, 10, 100, 1000, 10000, 50000. 5. Napisz funkcję obliczającą sumę pierwszych n liczb naturalnych. 6. Napisz funkcje wyznaczającą wartość n-tego elementu ciągu Fibonacciego. 0 1 = + =0 = 1! >1 7. (Zadanie dla osób chętnych i wytrwałych!) Obliczyć ręcznie silnie z 1000 i sprawdzić, czy komputer się nie pomylił.