Wstęp
Transkrypt
Wstęp
1. Wstęp. 1 Wstęp Bazą jest środowisko programowania Delphi firmy Borland International1, bazującego na języku Object Pascal. Wersja darmowa Delphi 7 Personal jest łatwo osiągalna w sieci. Po uruchomieniu aplikacji wybrać z menu File\New\Other.. Console Application (tworzenie nowej aplikacji). Pojawi się okienko, w którym będziemy pisać kod programu. program nazwa; //nagłówek {$APPTYPE CONSOLE} uses SysUtils; blok deklaracyjny (definicje) begin część wykonawcza (instrukcje do wykonania) end. Uwaga: nie usuwać wierszy oznaczonych kolorem czerwonym. 2 2. Składowe języka. Deklaracje i definicje. Instrukcja przypisania. 2. Składowe języka - deklaracje i definicje - instrukcja przypisania 2.1. Struktura blokowa programu i podprogramów Rysunek 2.1 przedstawia schemat struktury bloku głównego programu. Rys.2.1 Struktura programu źródłowego w języku Turbo Pascal Nagłówek programu jest opcjonalny (czyli nieobowiązkowy) i składa się z: - słowa kluczowego program i identyfikatora programu (nazwy) o długości do 255 znaków, - opcjonalnych parametrów programu w nawiasie - nazw plików wejścia i wyjścia (standardowo Input oraz Output). W sekcji deklaracji opisujemy elementy uŜywane przez program (deklaracje i definicje). Istotna jest ich kolejność, np. wcześniej musi wystąpić definicja typu, a potem deklaracja zmiennej tego typu. W sekcji instrukcji występują kolejne akcje jakie ma wykonać program. Instrukcje wykonywane są kolejno od pierwszej do ostatniej, pod warunkiem, Ŝe charakter instrukcji nie stanowi inaczej (np. iteracje, skoki, przekazanie wykonania do podprogramu itp.) UWAGI: • BEGIN to początek, a END to koniec sekcji instrukcji, • kropka kończy tekst programu źródłowego, teksty po kropce są ignorowane, • DEKLARACJE, DEFINICJE i INSTRUKCJE są oddzielane średnikami. 1. Wstęp. 3 2.2. Alfabet języka Na alfabet języka składają się: • duŜe i małe litery alfabetu łacińskiego (rozróŜnialne tylko dla stałych tekstowych) • cyfry 0 do 9 • jednoznakowe symbole specjalne: • + : ; * ( / ) = { < } > ^ [ # ] $ . @ , dwuznakowe symbole specjalne: <> <= >= := .. Podstawowe słowa kluczowe języka to: AND DOWNTO IF PROCEDURE TYPE ARRAY ELSE IN PROGRAM UNTIL BEGIN END LABEL RECORD USES CASE FILE MOD REPEAT VAR CONST FOR NOT SET WHILE DIV FUNCTION OF THEN WITH DO GOTO OR TO Słowa kluczowe w języku Pascal moŜna pisać małymi bądź duŜymi literami. 2.3. Identyfikatory (nazwy) Identyfikatory własne nadaje uŜytkownik dla programu, elementów przechowujących dane (zmiennych i stałych), typów, podprogramów, modułów. Identyfikatory to nazwy złoŜone z ciągu znaków. Identyfikatory standardowe (zastrzeŜone): • typów danych - boolean, char, integer, real, string i inne, • stałych (literałów) - false, true, maxint, • funkcji - abs, sin, cos, sqr, sqrt, exp i inne, • procedur - read, readln, reset, rewrite, write, writeln i inne, • plików - input, output. Zasady tworzenia identyfikatorów (nazw) własnych: • nazwa to ciąg znaków (liter, cyfr, znaku podkreślenia _ ), • nie wolno uŜywać spacji wewnątrz nazwy, • pierwszym znakiem musi być litera, • długość identyfikatora do 255 znaków, • małe i duŜe litery nie są rozróŜniane. 4 2. Składowe języka. Deklaracje i definicje. Instrukcja przypisania. Przykłady prawidłowych identyfikatorów: A C5 pom2 suma wynik e22 delta dzien_p IDENT1 ident1 To_jest_dlugi_identyfikator_lecz_poprawny UWAGI: • IDENT1 i ident1 są identyfikatorami tego samego elementu, • moŜna przedefiniować identyfikatory standardowe, lecz wtedy tracą one swoje znaczenie i nabierają nowego, nadanego przez uŜytkownika, np. moŜemy wprowadzić zmienną o nazwie sin lecz wówczas tracimy moŜliwość uŜywania funkcji sinus. 2.4. Komentarz Komentarz (własne uwagi lub opis działań) ma postać dowolnego ciągu znaków w nawiasach klamrowych lub od znaków // do końca wiersza: { dowolny komentarz } // dowolny komentarz do końca wiersza UWAGA: MoŜna dezaktywować niektóre instrukcje na etapie testowania programu zamykając je w nawiasach klamrowych. Przydatne jest i do dobrego tonu naleŜy umieszczanie komentarzy, lecz ich nadmiar powoduje zmniejszenie przejrzystości programu. 2.5. Separatory Separator nagłówka programu, deklaracji i instrukcji to średnik ; Separatory symboli języka to: spacja (lub ciąg spacji), ENTER, komentarz z tym Ŝe: • dwa dowolne słowa kluczowe, identyfikatory lub liczby bez znaku muszą być oddzielone co najmniej jednym separatorem, • ciąg spacji jest równowaŜny jednej spacji, • po instrukcjach, po których występuje słowo kluczowe END, średnik nie jest wymagany. 2.6. Stałe (literały) Stałe liczbowe - Stałe całkowite Stałą liczbową całkowitą jest ciąg cyfr (bez spacji pomiędzy cyframi!), opcjonalnie poprzedzony znakiem + lub –. Przykłady: –1 23234 012 0 –1234 1. Wstęp. 5 Stałą liczbową w zapisie szesnastkowym poprzedzamy znakiem $. Przykłady: $5 $FFF $1A12 - Stałe rzeczywiste Stałą liczbową rzeczywistą zapisujemy w dwóch formatach: • zapis stałopozycyjny [ + ]m.n − gdzie m i n to ciąg cyfr UWAGA: w nawiasach klamrowych { } - elementy do wyboru, w nawiasach kwadratowych [ ] - elementy opcjonalne (mogą zostać pominięte). Przykłady: 2.3336 –456.0 UWAGI: 1. Separatorem części całkowitej i ułamkowej jest kropka ! (nie przecinek) 2. NIE WOLNO: .45 brak części całkowitej 3. WOLNO: 234. brak 0 po kropce dziesiętnej • zapis zmiennopozycyjny e E [–]m[.n] [–]k gdzie m, n, k to ciąg cyfr. Litera E (lub e) interpretuje pomnoŜenie liczby dziesiętnej m.n przez 10 do potęgi k. Zapis zmiennopozycyjny (zwany teŜ naukowym) jest przydatny dla liczb bardzo duŜych i bardzo małych. Przykłady: –1.55e–6 3.4E7 {tzn. –1.55.10-6=0.00000155} {tzn. 3.4.107=34000000} Stałe łańcuchowe Stałą łańcuchową (tekstową) jest ciąg znaków ograniczonych apostrofami. Przykłady: 'Turbo Pascal' 'Kowalski' 'a' W stałych tekstowych małe i duŜe litery są istotne i rozróŜniane. Stałe logiczne Wartości logiczne nadawane zmiennym i wyraŜeniom typu logicznego boolean to: true prawda false fałsz 6 2. Składowe języka. Deklaracje i definicje. Instrukcja przypisania. 2.7. Klasyfikacja typów danych KaŜda stała, zmienna, wyraŜenie lub funkcja jest określonego typu - pozwala to uniezaleŜnić się od fizycznej reprezentacji obiektów w pamięci operacyjnej, umoŜliwia kontrolę dozwolonych wartości dla danego typu. Typ określa: • zbiór wartości, do którego naleŜy stała, bądź jakie moŜe przyjmować zmienna czy wyraŜenie lub jakie mogą być generowane przez funkcję, • zbiór operacji jakie mogą być wykonywane na danych tego zbioru. Typy danych oferowane przez język Turbo Pascal przedstawia rys. 2.2. Rys. 2.2 Klasyfikacja typów danych Typ standardowy to nazwany, zdefiniowany w języku zbiór wartości, z którego uŜytkownik moŜe korzystać bez definiowania. Typy strukturalne wprowadzone zostały w związku z praktycznymi potrzebami reprezentacji złoŜonych danych. W sekcji instrukcji wolno uŜywać tylko identyfikatorów standardowych i takich, których znaczenie określono w sekcji deklaracji. 2.7.1. Typy proste (porządkowe i rzeczywiste) Typy porządkowe Typy porządkowe zawierają skończony, uporządkowany zbiór wartości. 1. Wstęp. 7 A. Typy całkowite Typ ten przeznaczony jest dla zmiennych, które przechowują dane o wartościach naleŜących do zbioru liczb całkowitych. Podstawowy typ całkowity to typ integer. UŜywamy go w deklaracji zmiennych w przykładowej postaci: var x, y : integer ; Zakres wartości typu integer zaleŜy od implementacji – dla komputera PC stała Maxint określa maksymalną wartość dla tego typu. B. Typ znakowy - char Zmienne tego typu mogą przyjmować wartości pojedynczego znaku z tablicy ASCII. Przykład: var znak : char ; begin znak := 'c' ; {operacja przypisania – nadania wartości} ..... end. C. Typ logiczny - boolean Zmienne tego typu mogą przyjmować wartości stałych logicznych true lub false. D. Typ wyliczeniowy Typ wyliczeniowy jest typem prostym, porządkowym stanowiącym skończony, uporządkowany zbiór wartości, oznaczonych nazwami (identyfikatorami) wybranymi przez uŜytkownika: (lista wartości) Lista jest ciągiem nazw elementów, oddzielonych przecinkami. MoŜna zastosować opis typu bezpośrednio przy deklaracji zmiennych: var forma : (prostokat, kwadrat, elipsa, okrag) ; dzien : (pon, wto, sro, czw, pia, sob, nie) ; lub wcześniej zdefiniować typ: type figura = (prostokat, kwadrat, elipsa, okrag) ; dni = (pon, wto, sro, czw, pia, sob, nie) ; var forma : figura ; 8 2. Składowe języka. Deklaracje i definicje. Instrukcja przypisania. dzien : dni ; Dopuszcza się wykonywanie na elementach typu wyliczeniowego operacji przypisania, np.: forma := elipsa ; dzien := pon ; porównania (porządek według wyliczenia) np.: po < wt elipsa > kwadrat a takŜe uŜycia funkcji standardowych dla typów porządkowych, np.: pred(wt) ⇒ succ(cz) ⇒ ord(wt) ⇒ pon pia 1 {poprzedni element} {następny element} {kolejny numer elementu – liczony od 0} Dla elementów tego typu nie wolno stosować operacji arytmetycznych oraz wykorzystywać zmiennych tego typu w instrukcjach wprowadzania i wyprowadzania danych (read/write). E. Typ okrojony Typ okrojony oznacza podzbiór dowolnego typu porządkowego (boolean, char, integer, wyliczeniowego), zwanego typem bazowym: w1 .. w2 gdzie w1 ≤ w2 Przykład: type dni = (pon, wto, sro, czw, pia, sob, nie) ; var dzien : dni ; {zmienna bazowego typu wyliczeniowego} zakres : 1..10 ; {okrojenie typu integer} cyfra : '0'..'9' ; {okrojenie typu char} dni_wolne: sob.. nie ; Dozwolone operacje obowiązują jak dla typu bazowego. Typy rzeczywiste Typy rzeczywiste uŜywane są dla zmiennych przyjmujących wartość liczbową rzeczywistą. Podstawowym typem jest typ real. UWAGI: • opcje pakietu dopuszczają brak sprawdzania zakresów w trakcie wykonania programu, w menu Options – Compiler – Runtime errors – Range checking moŜna uaktywnić sprawdzanie zakresów, 1. Wstęp. • 9 przykładowo zmienne zajmują: char - 1 bajt pamięci, integer - 2 bajty, real - 6 bajtów. 2.7.2. Typ łańcuchowy - string Typ łańcuchowy umoŜliwia przechowywanie wartości typu tekstowego: string string [n] – do 255 znaków – deklaracja maksymalnej długości łańcucha n znaków Przykład deklaracji zmiennych tego typu: var napis nazwisko : string ; : string [30] ; 2.7.3. Typy strukturalne Typy strukturalne (tablicowy, rekordowy, zbiorowy, plikowy) to typy złoŜone zawierają wiele danych o typach prostych a takŜe strukturalnych, zostaną omówione osobno w dalszej części skryptu. 2.8. Deklaracje i definicje 2.8.1. Deklaracja modułów Moduły są to skompilowane biblioteki gotowych elementów (najczęściej procedur, funkcji i typów danych), które, po zadeklarowaniu modułu, moŜna wykorzystać w programie. uses lista_nazw_modułów ; np. uses graph ; uses crt, printer ; 2.8.2. Deklaracja nazwanych stałych i zmiennych inicjowanych Definicja nazwanej stałej: const nazwa1= wartość1; nazwa2=wartość2 ; ....; Wartość nazwanej stałej nie moŜe być zmieniana w trakcie przebiegu programu. Jej typ wynika z nadanej wartości. Definicja zmiennych inicjowanych: const nazwa1: typ= wartość1; nazwa2: typ=wartość2 ; ....; Wartość zmiennej inicjowanej moŜe być zmieniana w trakcie przebiegu programu. Przykłady: const e = 2.71828 ; n = 50 ; 10 2. Składowe języka. Deklaracje i definicje. Instrukcja przypisania. 2.8.3. Definicja typu Definicja typu jest nazwanym (własnym) opisem typu, uŜywanym potem m.in. w deklaracjach zmiennych. type nazwa_typu1 = opis_typu1 ; nazwa_typu2 = opis_typu2 ; .....; Przykład: type dni_tyg = (pon, wto, sro, czw, pia, sob, nie);{typ wyliczeniowy} licznik = 1..20 ; {typ okrojony całkowity} napis = string[30] ;{typ łańcuchowy o długości max. 30 znaków} 2.8.4. Deklaracje zmiennych Deklaracje wprowadzonych przez uŜytkownika zmiennych umieszczamy w bloku poprzedzonym słowem kluczowym var, w następującej, ogólnej postaci: var lista nazw zmiennych: nazwa_lub_opis_typu1; lista nazw zmiennych: nazwa_lub_opis_typu2 ; ....; Obiekty listy oddzielamy przecinkami. Przykładowe deklaracje: var x, suma, v15 wynik1 znak czy_jest nazwisko, imie alfa x5, y , z : integer ; {trzy zmienne typu całkowitego} : real ; {typ rzeczywisty} : char ; {typ znakowy} : boolean ; {typ logiczny} : napis ; { typ wcześniej opisany} : dni_tyg ; { typ wcześniej opisany} : licznik ; { typ wcześniej opisany} MoŜna równieŜ stosować opisy typów w deklaracji zmiennych, na przykład: var liczby dni : 1..23 ; : (pon, wto, sro, czw, pia, sob, nie) ; i wówczas definicja typów nie jest wymagana. W niektórych sytuacjach (przykładowo przy definiowaniu funkcji lub procedur), konieczne będzie wcześniejsze zdefiniowanie typów uŜywanych w programie. 1. Wstęp. 11 2.9. Instrukcja przypisania Instrukcja przypisania jest podstawową, prostą instrukcją w większości języków programowania. Za jej pomocą zmiennym nadawane są wartości w trakcie wykonywania programu. identyfikator_zmiennej := wyraŜenie ; gdzie := dwuznakowy operator przypisania. Najprostsza postać wyraŜenia to stała lub zmienna: wyraŜenie → stała wyraŜenie → zmienna Działanie instrukcji przypisania polega na obliczeniu wartości wyraŜenia po prawej stronie operatora i nadanie tej wartości zmiennej o identyfikatorze po lewej stronie znaku przypisania. UWAGA: Zachodzi konieczność zachowania zgodności typów zmiennej i wyraŜenia (wyjątkiem jest np. dopuszczenie przypisania zmiennej typu rzeczywistego wartości wyraŜenia typu całkowitego). Przykłady prawidłowych instrukcji przypisania: program p2_p; var x, y : real ; licznik : integer ; znak : char ; napis : string ; czy : boolean ; dzien : (pon, wto, sro, czw, pia, sob, nie) ; liczba : 1..100 ; begin x := 2.56 ; y := x ; {przypisanie zmiennej y tej samej wartości co x} licznik := 234 ; znak := 'A' ; czy := true ; dzien := czw ; liczba := 34 ; napis := 'To jest mój program' ; {..... dalsza część programu} end . 12 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. 3. WyraŜenia arytmetyczne i logiczne operacje wejścia i wyjścia 3.1. Definicja wyraŜenia WyraŜenia arytmetyczne słuŜą do zapisu wykonywania operacji obliczeniowych w trakcie przebiegu programu. WyraŜeniem arytmetycznym moŜe być stała, zmienna lub zapis złoŜonej operacji na stałych, zmiennych i funkcjach (standardowych, bibliotecznych lub własnych uŜytkownika) z uŜyciem operatorów arytmetycznych. Bardziej skomplikowane wyraŜenia zawierają operatory i funkcje. Ponadto uŜywamy nawiasów (tylko okrągłych!) w celu zmiany kolejności działań. Definicja rekurencyjna wyraŜenia (czyli definiowanie przez samą siebie) ma postać: wyraŜenie → stała wyraŜenie → zmienna wyraŜenie → identyfikator_funkcji (wyraŜenie) wyraŜenie → wyraŜenie operator wyraŜenie Z definicji wynika, Ŝe wyraŜenia po prawej stronie definicji mogą mieć kaŜdą z postaci definiowanych, a zatem np. argument funkcji moŜe być np. inną funkcją (tzw. zagnieŜdŜanie funkcji), a operatorami moŜna łączyć wiele wyraŜeń. WyraŜenia uŜywane są najczęściej w instrukcjach przypisania po prawej stronie symbolu :=. Dopuszczalne jest uŜycie wyraŜeń arytmetycznych (takŜe logicznych) jako parametru standardowej procedury wyprowadzania danych (write, writeln), a takŜe w innych instrukcjach zgodnie z zasadami ich składni. 3.2. Operatory arytmetyczne RozróŜniamy następujące operatory: • jednoargumentowe − zmiana znaku, + powielenie znaku, • dwuargumentowe multiplikatywne ∗ mnoŜenie, / dzielenie (rzeczywiste), div dzielenie całkowite (obydwa argumenty operacji muszą być całkowite, wynik jest całkowity), mod reszta z dzielenia całkowitego (jak wyŜej), 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. 13 (znak przedstawia spację), addytywne + – Przykłady: 7/2 →3.5 7 div 2 → 3 dodawanie, odejmowanie. 4/2 → 2.0 17 mod –5 → 2 6.5/2 → 3.75 3.5*x–6/(4–x)+5 Kolejność wykonywania operacji określają następujące reguły: WyraŜenia w nawiasach wykonywane najwcześniej (od najbardziej wewnętrznych), • Kolejność operacji (od najwcześniej wykonywanych): 1. jednoargumentowe, 2. multiplikatywne, 3. addytywne. • UWAGA: • Dla operatorów tej samej wagi - kolejność działań od lewej do prawej. • JeŜeli w wyraŜeniu są tylko argumenty całkowite i nie ma dzielenia rzeczywistego to wynik jest typu integer. • JeŜeli występuje chociaŜ jeden element typu real lub dzielenie rzeczywiste to wynik jest typu real. Na elementach tekstowych (char, string) moŜemy dokonywać operacji łączenia tekstów (tzw. konkatenacja) uŜywając znaku +. Przykład: nazwisko := 'Kowalski' ; tekst_1 := 'Pan' + nazwisko ; W rezultacie zmienna tekst_1 przyjmie wartość tekstową 'Pan Kowalski'. 3.3. Funkcje standardowe Istnieje moŜliwość uŜycia w wyraŜeniu funkcji standardowych (w kontekście identycznym jak zmienne proste) w postaci: identyfikator_funkcji (argument) Argumentem moŜe być wyraŜenie odpowiedniego typu. Zestaw funkcji standardowych przedstawia tabela 3.1. 14 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. Tabela 3.1 Funkcje standardowe Znaczenie wartość bezwzględna pierwiastek kwadratowy kwadrat ex logarytm naturalny sinus cosinus Arcus tangens Nazwa Typ wyniku Typ argumentu Przykład funkcji całkowity całkowity abs abs(-2) ⇒ 2 rzeczywisty rzeczywisty sqrt rzeczywisty rzeczywisty sqrt(5.45) sqr exp rzeczywisty rzeczywisty rzeczywisty rzeczywisty sqr(x-5) exp(-x/2) ln rzeczywisty rzeczywisty ln(2∗x-4) sin cos arctan rzeczywisty rzeczywisty rzeczywisty round trunc succ pred rzecz. (radiany) rzecz. (radiany) rzeczywisty całkowity rzeczywisty rzeczywisty całkowity rzeczywisty rzeczywisty całkowity rzeczywisty całkowity rzeczywisty porządkowy porządkowy porządkowy porządkowy round(3.6) ⇒ 4 trunc(3.6) ⇒ 3 succ ( 'g' ) ⇒ 'h' pred( 'j' ) ⇒ 'i' chr znakowy chr(49) ⇒ '1' ord length całkowity znakowy łańcuchowy całkowity część całkowita int część ułamkowa zaokrąglenie obcięcie znak następny znak poprzedni znak o podanym kodzie ASCII kod znaku długość tekstu frac całkowity sin(3∗alfa) cos(beta/2) arctan(fi) int(2.5) ⇒ 2.0 frac(3.6) ⇒ 0.6 ord( '1' ) ⇒ 49 length( 'alfa' ) ⇒ 4 W wyraŜeniach istotne są wszelkie ograniczenia obszaru określoności funkcji, np. uŜycie ln(-3), sqrt(-5.0) spowoduje błędy wykonania. Brak w języku operatora lub funkcji standardowej potęgowania, stąd stosuje się: x4 ⇒ x∗x∗x∗x lub sqr (x)∗sqr (x) lub sqr (sqr (x)), MoŜna zastosować funkcję power(podstawa, wykładnik) – potrzeba deklaracja biblioteki math w seksji uses, np: program test; uses sysutils, math; integer:x; begin x:=power(5.7, 6.89); 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. 15 writeln (x); readln; end. w przypadku wyŜszych lub niecałkowitych potęg stosujemy zapis: xy ⇒ exp (y∗ln (x)) Brak w języku równieŜ innych, często potrzebnych funkcji, jak na przykład tangens czy logarytm dziesiętny. Trzeba wówczas korzystać z elementarnych wzorów matematycznych: tg x = sin x cos x log x = ln x ln 10 NaleŜy zwrócić uwagę na moŜliwość wystąpienia przekroczenia dopuszczalnych zakresów wartości zmiennych przy obliczeniach. PoniŜsze wyraŜenie w postaci matematycznej: 5 x 2 − 2 sin2 x + 1,5 ⋅ 10 −5 x + 2 − 2 + y 1,5 moŜna zapisać w przykładowej postaci: (5∗x∗x – 2∗sin(x)*sin(x)+1.5e–5)/(abs(sqrt(x+2)–2)+exp(1.5∗ln(y))) W przypadku zastosowania w instrukcji przypisania wyraŜenia zawierającego zmienną, której aktualnie przypisujemy wartość, np.: x := x + 5 ; obliczana jest wartość wyraŜenia (wartość x powinna być wcześniej określona), a następnie uaktualniana wartość zmiennej, np. w ciągu instrukcji: ... x := 5 ; x := sqr(x) ; {x będzie równe 25} x := x +2 ; {powiększenie poprzedniej wartości x o 2, x będzie równe 27} ... Pisząc wyraŜenie naleŜy szczególnie uwaŜać na: - operatory arytmetyczne – szczególnie mnoŜenia: 2*x a nie 2x - format wykładniczy – np.1.34E-8 (107 to 1e7 a nie e7), - argumenty funkcji trygonometrycznych – podajemy je w radianach, w przypadku konieczności uŜywania kąta w stopniach naleŜy przeliczyć kąt na radiany wyraŜeniem stopnie*pi/180, (pi jest predefiniowaną nazwą stałej). - hierarchię działań – odpowiednie stosowanie nawiasów, 16 - 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. brak w języku operacji potęgowania, brak funkcji tangens, cotangens, logarytm dziesiętny, róŜnicę między funkcjami o podobnych nazwach – sqr i sqrt. 3.4. WyraŜenia logiczne WyraŜeniem logicznym moŜe być: 1. stała logiczna true ⇔ prawda false ⇔ fałsz 2. zmienna typu boolean, 3. relacja - porównanie w sensie liczbowym lub tekstowym (kolejności alfabetycznej) według schematu: wyraŜenie1 operator_relacji wyraŜenie2 Relacja ma wartość logiczną true lub false. Operatorami relacji są: < = > <= >= <> „róŜny” Przykłady relacji: x12 < 2 (4∗x+1.5) < 30.7 nazwisko >= 'G' {nazwiska od litery G do końca alfabetu} UWAGA: NaleŜy unikać operatorów < , =, >dla typów rzeczywistych ze względu na przybliŜenie wartości. 4. stałe logiczne, zmienne logiczne, relacje (w nawiasie!), połączone operatorami logicznymi: - jednoargumentowymi: not (negacja) np. not(i < 5) notobecny - dwuargumentowymi: and (iloczyn logiczny –"i" – jednoczesne spełnienie warunków) np. (x > 0)and(x < 3) or (suma logiczna –"lub" – alternatywne spełnienie warunków) np. (x < 0)or(x > 100) xor (róŜnica symetryczna – suma „modulo2”), (gdzie znak przedstawia spację). PoniŜsza tabela przedstawia wartości wyraŜeń logicznych w zaleŜności od wartości argumentów: 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. 17 Argument 1 Argument 2 and or xor false false false false false true false false true true false true false true true true true true true false W operacji przypisania wartości zmiennej logicznej musi zachodzić zgodność typów. Wartość wyraŜenia logicznego moŜna przypisać zmiennej typu boolean bądź uŜyć je w instrukcjach warunkowych lub iteracyjnych (dalsza część skryptu). Przykład: program p3 ; var zwolniony, obecny : boolean ; ocena : real ; liczba_obecnosci : integer ; begin ocena := 3.5 ; liczba_obecnosci := 12 ; obecny := liczba_obecnosci>=10 ; zwolniony := (ocena >= 4.5) andobecny ; writeln(zwolniony) end. 3.5. Standardowe procedury wejścia/wyjścia (wczytywania i wyprowadzania danych) Do wprowadzenia (standardowym urządzeniem wejścia jest klawiatura) wartości zmiennej w trakcie wykonania programu słuŜy instrukcja (wykonanie procedury standardowej) read(readln) – „czytaj”, o postaci: read [(lista zmiennych)] ; readln gdzie {} – elementy do wyboru, [ ] – element opcjonalny. Przykłady: readln (x1, x2, x3, y) ; read(x) ; readln ; {brak listy – moŜna podać cokolwiek – taka instrukcja słuŜy do chwilowego zatrzymania programu} Oczywiście uŜyte zmienne powinny być zadeklarowane. Po napotkaniu instrukcji read (readln) program zatrzymuje się i oczekuje podania danych dla zmiennych z listy (odpowiednich typów!). Podawane dane oddzie- 18 3. WyraŜenia arytmetyczne i logiczne. Operacje wejścia i wyjścia. lamy co najmniej jedną spacją lub w osobnych wierszach. JeŜeli wpiszemy zbyt wiele liczb, to nadwyŜka jest pamiętana dla następnej instrukcji read, natomiast w instrukcji readln czytane jest tyle wartości ile elementów zawiera lista, a reszta zostaje zignorowana. NaleŜy uwaŜać na zgodność typów zmiennych i danych (stała całkowita moŜe być wczytana do zmiennej rzeczywistej lecz nie odwrotnie). Instrukcją (procedurą standardową) wyprowadzania danych (domyślnie na ekran) jest write(writeln) – „pisz” – o strukturze: write [ (lista elementów) ] ; writeln Po wykonaniu instrukcji write kursor pozostaje na końcu wiersza, zaś po writeln następuje zmiana wiersza. Instrukcja writeln bez parametrów wyprowadza pusty wiersz. Elementami listy są stałe, zmienne i wyraŜenia, wraz z opcjonalnym określeniem formatu wyprowadzenia o postaci: wyraŜenie_typu_całkowitego:m gdzie m to wyraŜenie całkowite określające szerokość pola (liczba znaków), dla liczb typu real m ≥ 8, jeŜeli mniejsze (np. 0) to szerokość dopasowana będzie do niezbędnej liczby cyfr do zapisu wartości (z ewentualnym znakiem), wyraŜenie_typu_całkowitego:m:n m - jak wyŜej, n - liczba cyfr po kropce dziesiętnej (tylko dla typu real !). Przykład: program p4 ; var x : real ; y : string ; begin x := -21.546 ; y := 'Napis=' ; writeln ; writeln(x) ; writeln(x:8) ; writeln(x:10:3) ; writeln(y:10, 'TurboPascal':12) ; end. {wyprowadzenie pustego wiersza) {⇒ –2.154600000E+01} {⇒ –2.2E+01} {⇒ -21.546} {⇒ Napis= TurboPascal} { - to spacja} 4. Schematy blokowe. Instrukcje warunkowe. 19 4. Schematy blokowe - instrukcje warunkowe 4.1. Elementy graficzne schematów blokowych Schematy blokowe słuŜą do sporządzenia graficznej ilustracji tworzonego algorytmu. Poszczególne operacje (wprowadzanie danych, analiza warunków, iteracje) przedstawiane są w postaci bloków symbolizujących odpowiednią operację, które są połączone strzałkami, odzwierciedlającymi kolejność wykonania operacji. PoniŜszy rysunek przedstawia podstawowe, stosowane w schematach blokowych elementy graficzne, które posłuŜą do wyjaśnienia mechanizmu instrukcji warunkowych i iteracyjnych. Start i stop START STOP Operacje wejścia i wyjścia wczytaj a,b,x Instrukcja wykonawcza (proces) pisz suma, wynik Blok decyzyjny oblicz suma:=a+b czy suma>0 TAK NIE Rys.4.1 Elementy graficzne uŜywane w schematach blokowych. 20 4. Schematy blokowe. Instrukcje warunkowe. 4.2. Klasyfikacja instrukcji Rys. 4.2 Klasyfikacja instrukcji w języku Turbo Pascal 4.3. Instrukcje warunkowe 4.3.1. Instrukcja if.. then.. (jeŜeli.. to..) Instrukcja warunkowa słuŜy do warunkowego wykonywania dowolnej instrukcji (grupy instrukcji). UŜywamy dwóch postaci instrukcji. Postać skrócona: if WB then Instrukcja ; ”jeŜeli” ”to” W instrukcji wykonywane jest badanie warunku logicznego WB, a następnie: - jeśli WB = true to Instrukcja jest wykonywana, - jeśli WB = false to Instrukcja jest ignorowana. Postać pełna: if WB then Instrukcja_1 else Instrukcja_2 ; ”jeŜeli” ”to” ”w przeciwnym przypadku” W instrukcji wykonywane jest badanie warunku logicznego WB, a następnie: - jeśli WB = true to wykonywana jest Instrukcja_1, 4. Schematy blokowe. Instrukcje warunkowe. 21 - jeśli WB = false to wykonywana jest Instrukcja_2. Instrukcja if.. then.. naleŜy do grupy instrukcji strukturalnych – w jej skład wchodzi inna instrukcja (prosta lub strukturalna). Schematy blokowe ilustrujące działanie obu postaci instrukcji ilustruje rys.4.3. postać skrócona postać pełna Czy WB jest prawdą ? Czy WB jest prawdą ? NIE NIE TAK TAK Instrukcja1 Instrukcja2 Instrukcja Rys.4.3 Schemat blokowy istrukcji if.. then.. Przykład 1: program p4_1 ; uses crt ; var x : real ; begin clrscr ; readln(x) ; if x >= 0 then writeln ('Pierwiastek wynosi:', sqrt(x):10:3) else writeln ('x jest ujemne') ; readln end. W powyŜszym przykładzie instrukcja wyprowadzająca wartość pierwiastka zostanie wykonana jeŜeli zajdzie warunek x >= 0, w przeciwnym wypadku zostanie wykonana instrukcja wyprowadzająca komunikat, Ŝe x jest ujemne. Instrukcje wewnętrzne w instrukcji if..then.. mogą takŜe być innymi instrukcjami if..then... Taką postać stosujemy w przypadku konieczności wykonania 22 4. Schematy blokowe. Instrukcje warunkowe. róŜnych instrukcji dla trzech przedziałów wartości zmiennej sprawdzanej w wyraŜeniu logicznym. Przykład zagnieŜdŜania instrukcji if..then..: program p4_2a ; var x, y : real ; begin readln (x) ; if x >0 then y := 5 else if x<0 then y := 6 else y := 7 ; readln end . Po wykonaniu powyŜszej instrukcji if..then.. odbędą się przypisania: y → 5 dla x > 0 y → 6 dla x < 0 y → 7 dla x = 0 W przypadku zagnieŜdŜania instrukcji if..then.. sytuacja czasem się komplikuje, jak w poniŜszym przykładzie: program p4_2b ; var x : real ; begin readln(x) ; if x>=0 then if x=<100 then writeln('Liczba jest w przedziale 0 do 100') else writeln('Poza przedziałem') else writeln('Poza przedziałem') ; end. Oczywiście prościej moŜna zapisać powyŜszą instrukcję w postaci: if (x>=0) and (x<=100) then writeln('Liczba jest w przedziale 0 do 100') else writeln('Poza przedziałem') . 4.3.2 Instrukcja wyboru – case..of.. Instrukcja case umoŜliwia alternatywne wykonanie wielu instrukcji w zaleŜności od wartości wyraŜenia typu porządkowego. Postać instrukcji: 4. Schematy blokowe. Instrukcje warunkowe. case w s1 s2 .... [ else end ; 23 of :Instrukcja_1 ; :Instrukcja_2 ; Instrukcja_k ] w - wyraŜenie–selektor typu porządkowego (np. całkowitego, znakowego), s - stałe wyboru (etykiety wyboru) w moŜliwej postaci: s - pojedyncza wartość, s1, s2, sn - lista wartości, s1..s2 - zakres wartości (okrojenie), połączenie listy i zakresów, np. s1..s2, s3 , s4. Działanie instrukcji polega na obliczeniu wartości wyraŜenia w i w zaleŜności od wyniku wykonywana jest instrukcja odpowiadająca wartości (wartościom, zakresowi) stałej wyboru. W listach wyboru moŜna powtarzać te same wartości, jeśli aktualna wartość stałej wyboru naleŜy do listy to wykonywana jest właściwa instrukcja i dalsza analiza jest ignorowana. Instrukcja występująca po słowie kluczowym else wykonywana jest dla wartości nie występujących wcześniej. Przykład: program p4_3 ; var miesiac : 1..12 ; dni : 1..31 ; begin write ('Podaj numer miesiąca:') ; readln(miesiac) ; case miesiac of 2: dni := 28 ; {pojedyncza wartość} 4, 6 , 9, 11: dni := 30 ; {lista} 1..12: dni := 31 ; {zakres} {lub else dni := 31} end ; writeln ('Miesiac':8, miesiac:4, 'ma':3, dni:4, 'dni':4) ; case dni mod 2 of 0: writeln ('Liczba dni jest parzysta') ; 1: writeln ('Liczba dni jest nieparzysta') end ; readln end . 24 4. Schematy blokowe. Instrukcje warunkowe. 4.4. Instrukcja złoŜona (sekwencji) Przy konieczności wykonania więcej niŜ jednej instrukcji wewnątrz dowolnej instrukcji strukturalnej stosujemy tzw. instrukcję złoŜoną (sekwencji): begin ciąg instrukcji end Przykład: if x >= 0 then begin y := y+5 ; writeln(y:10:3) end else writeln('x jest ujemne') ; W powyŜszym przykładzie obie instrukcje pomiędzy słowami kluczowymi begin i end zostaną wykonane jeŜeli zajdzie warunek x>=0, w przeciwnym przypadku zostanie wykonana instrukcja wyprowadzająca komunikat, Ŝe x jest ujemne. W celu uzyskania przejrzystości programów w instrukcji złoŜonej i innych instrukcjach strukturalnych wykonujemy odpowiednie wcięcia wierszy tekstu programu źródłowego. 5. Instrukcje iteracyjne 25 5. Instrukcje iteracyjne 5.1. Zasada iteracji Instrukcje iteracyjne (zwane popularnie „pętlami”) słuŜą do wielokrotnego wykonywania jednej lub wielu instrukcji. W języku Turbo Pascal wyróŜniamy trzy typy instrukcji iteracyjnych: for..do.., repeat..until.. i while..do.. 5.2. Instrukcja for.. do.. (dla.. wykonuj..) Wersja z to Wersja z downto Z:= W1 Z:= W1 TAK TAK Z> W2 NIE Z< W2 NIE Instrukcja Z:= następne Z Instrukcja Z:= poprzednie Z Rys. 5.1 Schemat blokowy działania instrukcji for..do.. Postać instrukcji for..do.. jest następująca: 26 5. Instrukcje iteracyjne to W2 do Instrukcja ; downto for Z := W1 gdzie: Z – zmienna sterująca, typu porządkowego (całkowitego, znakowego, wyliczeniowego), W1 i W2 – wyraŜenia tego samego typu. Wartość zmiennej sterującej zmienia się od W1 do W2 narastająco (to) lub malejąco (downto), z krokiem 1 w przypadku zmiennej całkowitej lub co znak w przypadku zmiennej typu char. Np. x := 0 ; i1:=1 ; i2:=3 ; for i := i1 to i2 do x := x+5 ; {po wykonanej iteracji x przyjmie wartość 15} Instrukcja for..do.. jest stosowana gdy z góry znana jest liczba powtórzeń. Wewnątrz iteracji nie wolno zmieniać wartości zmiennej sterującej lecz wolno (i często się to robi) wykorzystywać zmienną sterującą w wyraŜeniach. Instrukcją wykonawczą iteracji moŜe być dowolna instrukcja, takŜe inna instrukcja for..do.. (zagnieŜdŜanie iteracji) lub instrukcja złoŜona. Wewnętrzna iteracja moŜe oczywiście zawierać ograniczenia licznika, będące wyraŜeniami związanymi z licznikiem pętli zewnętrznej. Przykład: program p5_1 ; var i, j : integer ; begin for i := 1 to 10 do begin for j := i+2 to 3*i do write('∗') ; writeln end end . Zmienna sterująca i przyjmuje wartość 1 i wykonywane są instrukcje wewnętrzne (w tym wewnętrzne for..do..), następnie wartość i zwiększana jest o 1 i ponownie są wykonywane instrukcje wewnętrzne. Drugi przykład ilustruje uŜycie zmiennej sterującej w wyraŜeniach: program p5_2 ; 5. Instrukcje iteracyjne var i z 27 : integer ; : real ; begin z := 2 ; for i := 1 to 3 do begin z := z + 3 * i ; writeln (z :5*i :2) end end . Efektem programu będzie wyprowadzenie wartości: 5.00 (na 5-ciu pozycjach) 11.00 (na 10-ciu pozycjach) 20.00 (na 15-tu pozycjach) 5.3. Instrukcja repeat..until.. (powtarzaj .. aŜ..) Postać ogólna instrukcji: repeat „powtarzaj” ciąg instrukcji until WB ; „aŜ” Ciąg instrukcji wewnętrznych w instrukcji repeat..until.. jest powtarzany dopóty, dopóki wyraŜenie logiczne WB posiada wartość false, zmiana na true powoduje zakończenie pętli. Sprawdzanie warunku odbywa się kaŜdorazowo po wykonaniu ciągu instrukcji, stąd iteracja ma zawsze co najmniej jeden przebieg. Jedna z instrukcji powinna mieć wpływ na wartość wyraŜenia logicznego, w przeciwnym przypadku iteracja nigdy się nie skończy. Jeśli błędny warunek spowoduje nieskończoną pętlę moŜemy przerwać wykonywanie programu kombinacją klawiszy CTRL+Break. Rysunek 5.2 przedstawia schemat blokowy działania iteracji repeat.. until.. . 28 5. Instrukcje iteracyjne ciąg instrukcji WB TAK NIE Rys.5.2 Schemat blokowy instrukcji repeat.. until.. Przykład: program p5_3 ; var x : integer ; begin x := 3 ; repeat x := x + 5 ; writeln(x:10) until x > 100 ; end . Program wydrukuje w wierszach kolejne wartości 8, 13.... 103. Iterację repeat.. until.. uŜywa się często do sprawdzania poprawności wprowadzanych danych: program p5_4 ; var znak : char ; begin repeat write ('Wprowadź małą literę : ') ; readln(znak) until (znak>='a') and (znak<='z') { dalsza część programu } end. 5. Instrukcje iteracyjne 29 5.4. Instrukcja while..do.. (dopóki.. wykonuj..) Postać ogólna instrukcji while..do..: while WB do Instrukcja ; "dopóki" "wykonuj" Działanie instrukcji while..do.. polega na powtarzaniu wykonywania wewnętrznej instrukcji, dopóki wyraŜenie logiczne WB posiada wartość true. Wartość ta jest sprawdzana kaŜdorazowo przed wykonaniem instrukcji wewnętrznej. Instrukcja wewnętrzna iteracji moŜe być instrukcją złoŜoną (begin.....instrukcje... end) jeśli zamierzamy wykonać więcej instrukcji wewnątrz iteracji. Podobnie jak w iteracji repeat..until.. istnieje konieczność wykonania w instrukcji (takŜe złoŜonej) takiej operacji, która wpływa na wartość wyraŜenia logicznego, aby iteracja mogła się kiedyś skończyć. Rysunek 5.3 przedstawia schemat blokowy działania iteracji while.. do.. . WB NIE TAK Instrukcja Rys.5.3 Schemat blokowy instrukcji while.. do.. Przykładowo w iteracji: while a>0 do x := 5 ; 30 5. Instrukcje iteracyjne instrukcja przypisania albo nigdy nie będzie wykonana albo iteracja będzie nieskończona, natomiast w instrukcji: while x>=0 do x := x – 2 ; pomniejszanie wartości zmiennej x daje gwarancję zakończenia iteracji po osiągnięciu przez zmienną wartości ujemnej. Jeśli błędny warunek spowoduje nieskończoną pętlę moŜemy przerwać program kombinacją klawiszy CTRL+Break. Przykładowy program oblicza sumę liczb parzystych od 2 do 100: program p5_5 ; var i, suma : integer ; begin i := 2 ; suma := 0 ; while i <= 100 do begin suma := suma + i ; i := i + 2 ; end ; writeln('Suma =', suma:5) end . Instrukcje iteracyjne są instrukcjami strukturalnymi (w ich skład wchodzi inna, dowolna instrukcja), zatem mogą się dowolnie wzajemnie zagnieŜdŜać, np.: program p5_6 ; var k, x : integer ; begin k :=5 ; repeat x :=10 ; while x>0 do x := x–1 ; k := k–1 ; until k=0 ; end. 6. Typ tablicowy array 31 6. Typ tablicowy - array Typ tablicowy jest typem strukturalnym (złoŜonym) stanowiącym skończony zbiór elementów tego samego typu, o połoŜeniu określanym przez indeksy. Zmienna typu tablicowego odpowiada w sensie matematycznym macierzy. Opis typu: array[ lista typów indeksów ] of typ składowych ⇓ typ porządkowy ⇓ typ dowolny (bez plikowego) Przykład: var tab1: array [ 0..50 ] of integer ; {tablica 1-wymiarowa, 51 elementów typu całkowitego} tab2, tab3: array [1..20, 1..30] of real ; {tablice 2-wymiarowe, 600 elementów typu rzeczywistego} tab4: array [ boolean, zakres, dzien_tyg] of char ; {tablica 3-wymiarowa, zakres i dzien_tyg to wcześniej opisane typy porządkowe} Tablice posiadają wymiar zaleŜny od liczby indeksów (1, 2, 3–wymiarowe) oraz rozmiar zaleŜny od zakresu określoności indeksu w kaŜdym wymiarze (np. rozmiar – 10 kolumn, 15 wierszy). Wektor jest tablicą 1–wymiarową (kolumnową lub wierszową). Zmienne tablicowe całościowe mogą być wykorzystane jedynie w operacji przypisania (przy zgodności typów), np.: var tab1, tab2 : array [1..10, 1..20] of real ; begin { ... wypełnienie tablicy tab2 } tab1 := tab2 ; { tab1 takie jak tab2 } {... dalsza część programu } end . Inne operacje wykonywane być mogą tylko na składowych - jeśli są dozwolone dla ich typu. Odwołanie do elementu tablicy zapisuje się w postaci: nazwa_zmiennej [ indeks1, .... ] Do nadawania wartości poszczególnym elementom tablicy stosuje się instrukcje wprowadzania danych (read) i instrukcje przypisania. Często wykorzystuje się instrukcje for..do.., gdy poprzez zmienną sterującą iteracji moŜemy określić zmienność indeksu (lub indeksów dla tablic wielowymiarowych) oraz uŜyć 32 6. Typ tablicowy array zmiennej sterującej w wyraŜeniu, którego wartość jest przypisywana elementowi tablicy (jeśli typ elementów tablicy jest typem liczbowym). W tablicy dwuwymiarowej trudno jest określić, który wymiar opisuje wiersze, a który kolumny, gdyŜ wartości w pamięci operacyjnej układane są "liniowo". Wszystko zaleŜy od "umowy programowej" i odpowiednio dobierając zmienne sterujące zagnieŜdŜanych iteracji for..do.. moŜna wyprowadzić tablicę na ekran wierszami lub kolumnami. Przykład 1: program p6_1 ; var tab1 : array [1..5, 1..5] of real ; {lub type t55 = array [1..5, 1..5] of real ; var tab1 : t55 ; } i, k : 1..5 ; begin tab1[2, 3] := 0.1 ; {nadanie wartości elementowi tablicy} for i := 1 to 5 do tab1[1, i] := 0.0 ; {wypełnianie pierwszego wiersza zerami} for i := 1 to 5 do tab1[2, i] := 2 * i ; {wypełnianie drugiego wiersza kolejnymi liczbami parzystymi} for i := 1 to 5 do tab1[3, i] := 2 * i – 1 ; {wypełnianie trzeciego wiersza kolejnymi liczbami nieparzystymi} for i := 1 to 5 do tab1[i, i] := i / 2 ; {wypełnianie przekątnej głównej wartościami 0.5 1 1.5 itd.} for i := 1 to 5 do for k := 1 to 5 do tab1[i, k] := i+2*k ; {wypełnianie całej tablicy, zagnieŜdŜanie iteracji} for i := 1 to 5 do begin for k := 1 to 5 do write (tab1[i, k]:4:0) ; writeln end ; {wyświetlenie na ekranie całej tablicy} writeln ; for k := 1 to 5 do begin for i := 1 to 5 do write(tab1[i ,k]:4:0) ; writeln end ; { wyświetlenie na ekranie całej tablicy transponowanej – zamiana wierszy i kolumn} readln end . Przykład 2: program p6_2 ; type dni_tyg = (po, wt, sr, cz, pi, so, ni) ; 6. Typ tablicowy array 33 wek = array[dni_tyg, boolean] of real ; var x : wek ; begin {...} x[po, true] := 10.2 ; x[wt, false] := 34.0 ; writeln(x[wt, false]/100:20:5) ; if x[sr, false] > 3e2 then writeln('W środę za duŜo') ; {...} end . Oczywiście zmienna typu wek jest tablicą dwuwymiarową o rozmiarach 7x2. Przykład 3: program p6_3 ; type opis= (nazw, imie) ; {wyliczeniowy} var grupa : array[1..100, opis] of string[30] ; i : integer ; begin {...} grupa[1, nazw] := 'Kowalski' ; if grupa[i, imie] = 'Stanislaw' then writeln(grupa[i, nazw]:30, grupa[i, imie]:20) ; {...} end . Typ łańcuchowy string[N] moŜna przedstawić jako wektor znaków o deklaracji: type lancuch = array[0..N] of char ; W elemencie o indeksie 0 przechowywany jest znak, którego kod ASCII jest długością łańcucha. Dla dowolnej zmiennej łańcuchowej x określić moŜemy jej długość przez uŜycie wyraŜenia ord(x[0]) lub length(x). 34 7. Typ rekordowy - record 7. Typ rekordowy - record 7.1. Definicja typu rekordowego Typ rekordowy jest typem strukturalnym o strukturze jednowymiarowej i o składowych dowolnych typów zwanych polami, które posiadają swoje własne nazwy, unikalne w ramach jednego typu rekordowego. Zmiennych tego typu uŜywamy w przypadku konieczności utworzenia obiektu opisywanego zbiorem danych róŜnego typu. Opis typu: record lista_nazw_pól_1: typ1 ; lista_nazw_pól_2: typ2 ; ........ end ; Przykład: program p7_1 ; type data = record rok : 1900..2100 ; mies : 1..12 ; dzien : 1..31 end ; osoba = record nazw : string[20] ; imie1, imie2 : string[15] ; data_ur : data ; {typ rekordowy wcześniej zdefiniowany} mezczyzna : boolean ; rodzenstwo : array[1..5] of string[15] end ; var student : osoba ; begin {....instrukcje...} end . Pola rekordu mogą być tego samego lub róŜnego typu, takŜe rekordowego, tablicowego itp. Do pól rekordu istnieje dostęp bezpośredni, odwołanie do pola zmiennej odbywa się przez nazwę zmiennej rekordowej i nazwę pola (tzw. dostęp kwalifikowany), oddzielonych kropką: 7. Typ rekordowy - record 35 nazwa_zmiennej . nazwa_pola {. ewentualna_nazwa_pola_składowego ...} Przykład: student . nazw := 'Kowalski' ; if student .data_ur.rok<1975 then ...{pole data_ur jest typu rekordowego} UWAGI: • w jednym typie rekordowym wszystkie pola muszą mieć róŜne nazwy, • dopuszczalne jest całościowe przypisanie jednej zmiennej rekordowej wartości innej zmiennej rekordowej pod warunkiem, Ŝe są tego samego typu, • na zmiennych składowych moŜna dokonywać operacji dopuszczalnych dla ich typu. Tworzenie wielu zmiennych rekordowych dla zapamiętywania danych strukturalnych w trakcie przebiegu programu jest niewygodne, dlatego teŜ często dane tego typu gromadzi się w tablicy (najczęściej jednowymiarowej). JeŜeli rekordy znajdują się w tablicy, np.: type osoba : record nazwisko : string ; imie : string ; wiek : 0..100 end ; var grupa : array [1..30] of osoba ; to dostęp do pola pojedynczego rekordu, będącego elementem tej tablicy moŜe się odbyć w przykładowej postaci: writeln (grupa[5].nazwisko) {wczytanie tekstu do pola nazwisko w 5-tym elemencie tablicy} lub grupa[5].nazwisko= 'Kowalski' ; {przypisanie} Przykład: program p7_2 ; type osoba= record nazwisko, imie : string[20] ; data_ur : record dzien, miesiac, rok : integer ; end ; l_dzieci : 0..15 ; end ; var grupa : array[1..30] of osoba ; var i : 1..30 ; begin for i :=1 to 30 do begin 36 7. Typ rekordowy - record writeln ('Podaj dane ', i , '-ego studenta :') ; write ('Nazwisko:') ; readln (grupa[i].nazwisko) ; write ('Rok urodzenia:') ; readln (grupa[i]. data_ur.rok) ; {....itd. wypełnienie tablicy} end ; {wypisanie liczby dzieci dla osób urodzonych przed 1975 rokiem} for i :=1 to 30 do if grupa[ i]. data_ur. rok<1975 then writeln(grupa[i].nazwisko:30,grupa[i]. l_dzieci:4) ; end . 7.2. Instrukcja wiąŜąca - with.. do.. Instrukcja with upraszcza zapis dostępu do pól rekordu. Jej ogólna postać to: with lista_zmiennych_rekordowych do Istrukcja ; W instrukcji wewnętrznej (takŜe złoŜonej) moŜe nastąpić odwołanie bezpośrednio do nazw pól rekordów wymienionych w liście zmiennych, bez wyspecyfikowanych nazw zmiennych całościowych z listy. Przykład 1: program p7_3 ; var zespolona : record rzecz, uroj : real end ; begin with zespolona do begin rzecz := 2.5 ; uroj := 1.0 end ; {... dalsza część programu} end . Przykład 2: program p7_4 ; type osoba = record nr nazw end ; var j : 1..10 ; : integer ; : string[30] ; 7. Typ rekordowy - record student : osoba ; grupa : array[1..10] of osoba ; begin writeln(' Wczytanie danych do tablicy') ; for j := 1 to 10 do with student do begin write('Podaj numer:') ; readln(nr) ; write('Podaj nazwisko:') ; readln(nazw) ; grupa[ j ] := student end ; {...dalsza część programu..} end . 37 38 8. Sortowanie 8. Sortowanie 8.1. Prosty algorytm sortowania Algorytm polega na porównywaniu pierwszego elementu z kolejnymi, w następnym przebiegu iteracji drugiego elementu z kolejnymi itd., aŜ do porównania dwóch ostatnich elementów. W przypadku niewłaściwej kolejności następuje zamiana elementów miejscami. Dla przykładowego ciągu liczb, podlegającego sortowaniu (12, 5, 11, 4, 7, 2) kolejne analizy przebiegają następująco: 1 przebieg 12 5 5 12 5 12 4 12 4 12 2 12 2 przebieg 2 12 2 11 2 5 2 5 2 4 11 11 11 11 11 11 4 4 4 5 5 5 7 7 7 7 7 7 2 2 2 2 2 4 11 12 12 12 12 5 5 11 11 11 7 7 7 7 7 4 4 4 4 5 zamiana 4 zamiana 5 3 przebieg 2 4 2 4 2 4 2 4 12 11 7 5 11 12 12 12 7 7 11 11 5 5 5 7 zamiana 7 zamiana 8 zamiana 9 4 przebieg 2 4 2 4 2 4 5 5 5 12 11 7 11 12 12 7 7 11 zamiana 10 zamiana 11 5 przebieg 2 4 2 4 5 5 7 7 12 11 11 12 zamiana 12 Liczba analiz: n (n − 1), gdzie n - liczba elementów. 2 zamiana 1 zamiana 2 zamiana 3 zamiana 6 8. Sortowanie 39 8.2. Sortowanie bąbelkowe Algorytm polega na n-krotnym (gdzie n jest liczbą elementów zbioru) porównywaniu kolejnych par elementów sąsiadujących i zamianie miejscami w przypadku niewłaściwej kolejności: 1 przebieg 12 5 5 12 5 11 5 11 5 11 5 11 2 przebieg 5 11 5 11 5 4 5 4 5 4 5 4 3 przebieg 5 4 4 5 4 5 4 5 4 5 4 5 4 przebieg 4 5 4 5 4 2 4 2 4 2 4 2 5 przebieg 4 2 2 4 2 4 2 4 2 4 11 11 12 4 4 4 4 4 4 12 7 7 7 7 7 7 12 2 2 zamiana 1 2 zamiana 2 2 zamiana 3 2 zamiana 4 2 zamiana 5 12 efekt ostatniej zamiany 4 4 11 7 7 7 7 7 7 11 2 2 2 2 2 2 11 11 12 12 12 12 12 12 7 7 7 2 2 2 2 2 2 7 7 7 11 11 11 11 11 11 12 12 12 12 12 12 2 2 5 5 5 5 7 7 7 7 7 7 11 11 11 11 11 11 12 12 12 12 12 12 5 5 5 5 5 7 7 7 7 7 11 11 11 11 11 12 12 12 12 12 zmiana 6 zmiana 7 zmiana 8 zamiana 9 zamiana 10 zamiana 11 zmiana 12 40 8. Sortowanie 2 4 6 przebieg 2 4 2 4 2 4 2 4 2 4 5 7 11 12 5 5 5 5 5 7 7 7 7 7 11 11 11 11 11 12 12 12 12 12 8.3. Sortowanie bąbelkowe skrócone Algorytm polega na (n–1)-krotnym porównywaniu kolejnych par elementów sąsiadujących, z tym, Ŝe w kaŜdym kolejnym przebiegu liczba analizowanych par jest zmniejszana o 1: 1 przebieg 12 5 5 12 5 11 5 11 5 11 5 11 2 przebieg 5 11 5 11 5 4 5 4 5 4 3 przebieg 5 4 4 5 4 5 4 5 4 przebieg 4 5 4 5 4 2 5 przebieg 4 2 2 4 11 11 12 4 4 4 4 4 4 12 7 7 7 7 7 7 12 2 2 2 2 2 2 12 4 4 11 7 7 7 7 7 11 2 2 2 2 2 11 12 12 12 12 12 7 7 7 2 2 2 2 7 11 11 11 11 12 12 12 12 2 2 5 7 7 7 11 11 11 12 12 12 5 5 7 7 11 11 12 12 zamiana 1 zamiana 2 zamiana 3 zamiana 4 zamiana 5 zamiana 6 zamiana 7 zamiana 8 zamiana 9 zamiana 10 zamiana 11 zamiana 12 9. Podprogramy - funkcje 41 9. Podprogramy - funkcje Stosowanie procedur i funkcji (podprogramów) umoŜliwia rozbicie złoŜonego ciągu operacji na mniejsze struktury. Zwiększa to czytelność programu, poprawia kontrolę nad rozbudowanym programem i umoŜliwia wielokrotne wykorzystanie bloków instrukcji ze zmienionymi danymi wejściowymi. Podprogramy mogą mieć strukturę hierarchiczną, a zatem kolejna procedura lub funkcja moŜe być definiowana wewnątrz innej. Podstawowym zadaniem funkcji jest obliczenie i zwrot do miejsca wywołania (przez nazwę funkcji) pojedynczej wartości zaś procedura wykonuje sekwencję działań i moŜe wyprowadzać wiele obliczonych wartości. Definicja procedury lub/i funkcji umieszczana jest w części deklaracyjnej programu głównego (takŜe innej procedury lub funkcji), jej struktura jest podobna do struktury programu głównego: function nazwa (lista parametrów formalnych): typ funkcji ; definicje, deklaracje begin .... nazwa := wyraŜenie ; end ; W nagłówku definicji funkcji muszą się znaleźć identyfikatory parametrów słuŜących do przekazania danych do wnętrza funkcji z określeniem ich typów (elementy listy oddzielane są średnikami) oraz typ funkcji (prosty). Parametry te zwane są formalnymi. Funkcja zapewnia w zasadzie zwrot pojedynczej wartości poprzez jej nazwę (obiekt określonego typu prostego), chociaŜ moŜna równieŜ stosować przekazanie przez zmienną (jak w procedurze - patrz następne ćwiczenie). W sekcji wykonawczej funkcji musi zatem znaleźć się instrukcja nadająca wartości funkcji (np. instrukcja przypisania dla nazwy funkcji). Przykładowe nagłówki definicji funkcji: function suma (x1, x2: real): real ; function sprawdzaj (x1, x2: real ; y1, y2: integer): boolean ; Wykonanie (wywołanie) funkcji odbyć się moŜe: • w wyraŜeniu odpowiedniego typu (funkcje typów liczbowych w wyraŜeniach arytmetycznych, funkcje logiczne w wyraŜeniach logicznych), • w procedurze wyprowadzenia write(writeln), 42 9. Podprogramy - funkcje • jako parametr innej funkcji lub procedury (w tym standardowej) z zachowaniem zgodności typu. Parametry wywołania (wykonania) funkcji, zwane parametrami aktualnymi lub argumentami, mogą być stałymi, zmiennymi lub wyraŜeniami, oddzielane są przecinkami. Liczba, typ i kolejność parametrów aktualnych musi być zgodna z parametrami formalnymi w deklaracji funkcji. Przykładowy program ilustruje moŜliwe sposoby wywołania funkcji: program p9_1 ; var a, b,c : real ; function iloczyn (x1, x2: real): real ; {definicja funkcji} begin iloczyn := x1*x2 ; {nadanie wartości funkcji} end ; {koniec definicji funkcji} begin {program główny} a := 3.4 ; b := 5.6 ; writeln (iloczyn(a, b)) ; {uŜycie funkcji jako parametru instrukcji writeln} c := 3 ∗ iloczyn (a, b) ; writeln (c:10:5) ; c := 2 ∗ iloczyn (b, 1– a)/ iloczyn (a, 2*b +1) ; {uŜycie funkcji w wyraŜeniu} writeln(c:10:5) ; c := 1/iloczyn (c–1, a) ; writeln(c:10:5) ; if iloczyn(a*a, b-3) > 0 then writeln('dodatni'); readln; end . Wywołanie (wykonanie) funkcji w programie dokonywane jest zatem tak jak uŜycie zmiennej (podobnie jak funkcje standardowe). Wartości parametrów aktualnych (lub obliczonych wyraŜeń) przekazywane są do funkcji (kopiowane na parametry formalne), następnie wykonywane są instrukcje funkcji i wartość obliczona zwracana jest pod identyfikatorem (nazwą) funkcji do miejsca wywołania. Wykonanie funkcji moŜe być teŜ osobną instrukcją, jeśli zwrot wartości funkcji nie jest potrzebny, a instrukcje funkcji wykonują teŜ inne, niezbędne działania, na przykład wyświetla na ekranie pewne informacje. PoniŜszy program tabelaryzuje wartości funkcji opisanej przedziałami, w 21 punktach zakresu (0.0, 2.0): program p9_2 ; var i : 0..20 ; 9. Podprogramy - funkcje function fun(x: real): real ; {definicja funkcji} var xkw : real ; begin xkw := x∗x ; if x <= 0.5 then fun := 0.2∗xkw-x+0.1 else fun := xkw/(xkw–0.1) end ; {koniec definicji funkcji} begin {początek programu głównego} writeln ; writeln ('Tabela wartości funkcji':45) ; writeln ; writeln ('x':30, 'f(x)':20) ; for i := 0 to 20 do begin writeln(i:12, i∗0.1:20:3, fun(i∗0.1):20:5) end ; readln; end . 43 44 10. Podprogramy - procedury 10. Podprogramy - procedury Procedury mogą wykonać sekwencję działań na wartościach wprowadzonych do jej wnętrza i wyprowadzać wiele obliczonych wartości. Definicja procedury, podobnie jak definicja funkcji umieszczana jest w części deklaracyjnej programu głównego (takŜe innej procedury lub funkcji): procedure nazwa (parametry formalne) ; definicje, deklaracje begin .... instrukcje .... end ; Parametry formalne słuŜą do przekazywania danych pomiędzy sekcją, w której wykonywana jest procedura, a samą procedurą. Niektóre z nich przekazują dane wejściowe do funkcji lub procedury, inne mogą zwrócić wyliczone w podprogramie wartości do programu głównego lub nadrzędnego podprogramu. Deklaracje parametrów formalnych są oddzielane średnikami, kaŜda deklaracja składa się z listy zmiennych i nazwy typu. Przykładowe nagłówki deklaracji procedury: procedure drukuj (x1, x2: real) ; procedure suma (liczba1, liczba2: real ; var wynik: real) ; Poprzedzające zmienną (lub listę zmiennych) słowo kluczowe var określa przekazanie danych przez zmienną, jego brak przekazanie danych przez wartość. Wykonanie procedury jest odrębną instrukcją. Parametry aktualne odpowiadające pozycji wywołania przez wartość mogą być stałymi, zmiennymi lub wyraŜeniami, natomiast parametry aktualne, odpowiadające przekazaniu przez zmienną, muszą być identyfikatorami (nazwami) zmiennych. Przykładowe instrukcje wykonania powyŜszych procedur: drukuj (5, 2*y); suma (7.8, 8.0, rez); Przekazanie przez wartość stosuje się w celu wniesienia danych wejściowych do podprogramu, natomiast przekazanie przez zmienną ma na celu zwrot wyników działania procedury do miejsca wywołania. Przekazanie przez wartość traktowane jest jako nadanie wartości początkowej zmiennej lokalnej, istnieją- 10. Podprogramy - procedury 45 cej tylko w trakcie wykonywania podprogramu, a usuwanych z pamięci w momencie jego zakończenia. Przekazanie przez zmienną moŜe przenieść wartość do procedury (wejście), po jej zakończeniu moŜe (lecz nie musi) spowodować zwrot nowej wartości do programu wywołującego procedurę. Przekazanie przez zmienną daje teŜ oszczędności pamięci, np. duŜe tablice przekazuje się do podprogramów zwykle przez zmienną. PoniŜej prosty przykład uŜycia procedury sumującej dwie liczby rzeczywiste: program p10_1 ; var a, b, c : real ; {definicja procedury} procedure suma (liczba1, liczba2 : real ; var wynik : real) ; begin wynik := liczba1 + liczba2 ; end ; {koniec definicji procedury} begin {początek programu głównego} a := 2 ; b := 2 ; {przykładowe wykonania procedury} suma (6.7, 2*a, c) ; {trzeci parametr aktualny musi być zmienną} writeln(c:10:3) ; {c jest równe 6.7+2*2=10.7} suma (a, b, c) ; writeln(c:10:3) ; {c jest równe 2+2=4} suma (c, c, c) ; writeln(c:10:3) ; {c jest równe 4+4=8} suma (c, b, a) ; writeln(a:10:3) ; {a jest równe 8+2=10} readln; end . Zasięg deklaracji zmiennych Deklaracje obiektów w programie głównym zwane są globalnymi, "widoczne" są w całym programie, takŜe i w podprogramach (czasami mogą być tam "przesłonięte" czyli ponownie deklarowane). Deklaracje obiektów w definicji podprogramu to deklaracje lokalne dla podprogramu, nie będą "widoczne" w programie wywołującym. Jeśli w ciele definicji podprogramu jest definicja zagnieŜdŜonego podprogramu to obiekty lokalne stają się dla niego zewnętrznymi. Zasięg deklaracji ilustruje rys.10.1. 46 10. Podprogramy - procedury program A deklaracje A procedure B deklaracje B procedure C deklaracje C begin deklaracje globalne – zmienne „widoczne” w całym programie (chyba Ŝe „przesłonięte” w B, C lub D) deklaracje lokalne w B (zewnętrzne dla C) deklaracje lokalne w C (zmienne z deklaracji A i B moŜna „przesłaniać”) end begin tu moŜna wykorzystać procedurę C end procedure D deklaracje D begin end begin deklaracje lokalne w D (zmienne z deklaracji A moŜna „przesłaniać”, procedura C „niewidoczna”) sekcja główna programu, tu moŜna wywołać procedury B i D, nie moŜna procedury C end . Rys 10.1. Zasięg deklaracji w Turbo Pascalu. 11. Pliki 47 11. Pliki 11.1. Typ plikowy Plik w pojęciu Turbo Pascala jest ciągiem elementów tego samego typu, odwzorowującym zbiór danych gromadzonych w pliku fizycznym, umieszczonym w pamięci zewnętrznej. Dostęp do pliku jest sekwencyjny, tzn. po jego otwarciu dostępny jest pierwszy zapisany w nim element, zaś inne elementy mogą być udostępniane tylko po wykonaniu pewnych operacji (czytanie kolejnych elementów, przesunięcie pozycji czytania itp.). Aby uzyskać dostęp do pliku fizycznego naleŜy zadeklarować w programie zmienną lub zmienne typu plikowego, które trzeba skojarzyć z plikami dyskowymi. Zmienne te moŜna kojarzyć kolejno z róŜnymi plikami. Plik moŜe mieć dowolny typ składowych (oprócz typu plikowego). Definicja typu plikowego ma jedną z postaci: type nazwa = file of typ ; plik elementowy (zdefiniowany) type nazwa = file ; plik blokowy (niezdefiniowany) type nazwa = text ; plik tekstowy (typ predefiniowany) Przykład uŜycia pliku elementowego: type osoba = record nazw : string [30] ; imie : string [30] end ; grupa = file of osoba ; {typ plikowy} var student : osoba ; gr1, gr2, gr3 : grupa ; {zmienne typu plikowego} begin {...operacje na pliku...} end . Dla całościowych zmiennych plikowych dopuszczalne są jedynie niektóre operacje systemu plików, natomiast na składowych, po ich przeczytaniu i przypisaniu zmiennym odpowiedniego typu, moŜemy wykonywać operacje w zaleŜności od tego typu. W pliku elementowym zapis dokonuje się w postaci binarnej, a w tekstowym nawet elementy liczbowe zapisywane są jako tekst ASCII. Obsługa plików wymaga następujących operacji: 48 11. Pliki • opisu zmiennej plikowej w części deklaracyjnej, • skojarzenia zmiennej plikowej z plikiem fizycznym, • otwarcia pliku (do zapisu lub do odczytu), • wykonania niezbędnych operacji (czytanie, zapis i inne), • zamknięcia pliku. 11.2. Operacje plikowe W poniŜszej tabeli przedstawiono podstawowe operacje plikowe z uŜyciem standardowych procedur i funkcji plikowych, w których uŜyto określeń: f - nazwa zmiennej typu plikowego, Wt - wyraŜenie tekstowe, W - wyraŜenie odpowiedniego typu, Z - nazwa zmiennej. Tabela 11.1 Podstawowe operacje plikowe Operacja Skojarzenie zmiennej plikowej z plikiem fizycznym Otwarcie do odczytu i zapisu (istniejący plik) Otwarcie do zapisu (nowy plik lub istniejący lecz zapisywany od nowa) Otwarcie do dopisania (na końcu pliku tekstowego) Zamknięcie pliku Detekcja końca pliku Odczyt Zapis Zapis (tylko plik typu tekstowego!) Typ Zapis Przykłady procedura assign (f , Wt) assign(p1,'C:\DANE\gr1.dan') ; assign(p2,'C:\DANE\gr2.ele') ; procedura reset (f) reset (p1) ; procedura rewrite (f) rewrite (p2) ; procedura append (f) append (p2) ; procedura funkcja logiczna procedura procedura close (p2) ; if eof (p1) then .... ; while not eof(p1) do ....; read (p1, student); write (p2, student); close (f) eof (f) read (f, Z1,..,Zn) write(f, W) procedura writeln(f, W) writeln (p1, student); UWAGI: • po otwarciu do odczytu wskaźnik czytania ustawia się na początku pliku, • po przeczytaniu elementu następuje przesunięcie wskaźnika odczytu do następnego elementu, aby przeczytać dowolny element wewnątrz pliku naleŜy uprzednio przeczytać elementy poprzedzające, 11. Pliki • • • • • • • 49 otwarcie nowego pliku procedurą rewrite powoduje utworzenie pustego pliku, jeśli plik istnieje zostanie wyzerowany, gdy eof(f) = true - wskaźnik odczytu (zapisu) znajduje się na końcu pliku, gdy po wykonaniu procedury reset okaŜe się Ŝe eof(file) = true to plik jest pusty, instrukcja writeln istnieje tylko dla pliku typu tekstowego, zapis dopuszczalny jest tylko na końcu pliku (niemoŜliwy zapis „w środku” pliku), kaŜdy zapis przesuwa wskaźnik o jedną pozycję, dopisanie „w środku” pliku wykonuje się przez etapowe przepisywanie do innego pliku. PoniŜszy program zawiera modyfikację przykładu p9.2 z uŜyciem procedury oraz zapisem tabeli obliczanych wartości do pliku tekstowego: program p11_1 ; uses crt ; var i : 0..20 ; y : real ; w : char ; plik : text ; procedure oblicz (x: real ; var f: real ; var wsk: char) ; {definicja procedury} var xkw : real ; begin xkw := x∗x ; if x <= 0.5 then f := 0.2∗xkw–x+0.1 else f := xkw/(xkw–0.1) ; if f < 0 then wsk := '−' else wsk := '+' end ; begin { początek sekcji wykonawczej} assign(plik, 'c:\tp\moje\fun.txt') ; { skojarzenie pliku } rewrite(plik) ; { otwarcie pliku do zapisu } clrscr ; writeln('Tabela wartości funkcji':45) ; writeln('x':30, 'f(x)': 20) ; for i := 0 to 20 do begin oblicz(i∗0.1, y, w) ; {wywołanie procedury} writeln(i:12, i∗0.1:20:3, y:20:5, w:3) ; writeln(plik, i:12, i∗0.1:20:3, y:20:5) ;{zapis do pliku} end ; close(plik) ; {zamknięcie pliku} 50 11. Pliki repeat until keypressed end . PoniŜszy przykład ilustruje wykorzystanie bardziej złoŜonych struktur i algorytmów. Jest to ilustracja uŜycia typu rekordowego wraz z zapisem/odczytem danych do/z pliku. Program wczytuje wprowadzane dane i zapisuje do tablicy rekordów, a następnie wyświetla listę danych posortowaną według zadanego pola. program p11_2 ; type tosoba = record { rekord zawierający dane osobowe } imie : string[ 20 ] ; nazwisko : string[ 15 ] ; adres : string[ 40 ] ; wiek : integer ; plec : (k,m) ; end ; const maxosob = 30 ; { maksymalny rozmiar tablicy z danymi osobowymi } var lista_osob : array [ 1..maxosob ] of tosoba ; ilosc_osob : integer ; nastepna : boolean ; znak : char ; i, n : integer ; zamienic : boolean ; pom : tosoba ; {zmienna pomocnicza do sortowania tablicy } f : file of tosoba ; nazwa : string ; begin {początek programu} repeat writeln('0 - wprowadzanie danych') ; writeln('1 - odczyt danych z dysku') ; write('Twój wybór [ 0/1 ]:') ; readln(znak) ; until (znak = '0') or (znak = '1') ; if znak = '0' then begin { wprowadzanie danych} nastepna := true ; ilosc_osob := 0 ; while nastepna and (ilosc_osob <= maxosob) do begin inc(ilosc_osob) ; {procedura zwiększająca ilosc_osob o 1} with lista_osob[ ilosc_osob ] do 11. Pliki 51 begin write('Imię:':12) ; readln(imie) ; write('Nazwisko:':12) ; readln(nazwisko) ; write('Adres: ':12) ; readln(adres) ; write('Wiek: ':12) ; readln(wiek) ; write('Płeć [ k/m ]:':12) ; repeat readln(znak) ; znak := upcase(znak) ; {funkcja upcase zamienia literę małą na duŜą} until (znak = 'K') or (znak = 'M') ; case znak of 'K': plec := k else plec := m end ; end ; {koniec with} if ilosc_osob < maxosob then begin writeln ; writeln('Wprowadziłeś do tej pory ', ilosc_osob,' osób') ; write('Czy chcesz podać następną osobę? [ t/n]') ; repeat readln(znak) ; znak := upcase(znak) ; until (znak = 'T') or (znak = 'N') ; nastepna := znak = 'T' ; end ; end ; {koniec while} writeln ; write('Zapisać wprowadzone osoby na dysku [ t/n ]?') ; repeat readln(znak) ; znak := upcase(znak) ; until (znak = 'T') or (znak = 'N') ; if znak = 'T' then begin{ zapis listy na dysk } write('Podaj ścieŜkę i nazwę pliku:') ; readln(nazwa) ; if nazwa <> '' {dwa apostrofy - pusty łańcuch} then begin assign(f , nazwa) ; rewrite(f) ; for i := 1 to ilosc_osob do write(f, lista_osob[ i ]) ; close(f) ; end ; 52 11. Pliki end ; end { koniec wprowadzania danych } else { odczyt z dysku } begin ilosc_osob := 0; repeat write('Podaj ścieŜkę i nazwę pliku:') ; readln(nazwa) ; until nazwa < > '' ; {dwa apostrofy} assign(f, nazwa) ; {przypisanie zmiennej plikowej do pliku dyskowego} reset(f) ; {otwarcie pliku do odczytu} while not eof(f) and (ilosc_osob < maxosob) do begin inc(ilosc_osob) ; {powiększenie ilosc_osob o 1} read(f, lista_osob [ilosc_osob ]) ; end ; close(f) ; {zamknięcie pliku} end ; { koniec odczytu z dysku } {sortowanie i wyświetlanie listy } repeat {pętla róŜnego sposobu sortowania} {wybór sposobu sortowania } writeln('Wybierz porządek sortowania:') ; writeln('I - imię') ; writeln('N - nazwisko') ; writeln('W – wiek') ; writeln('P – płeć') ; readln(znak) ; znak := upcase(znak) ; case znak of 'I' : writeln('Lista posortowana według imion') ; 'N' : writeln('Lista posortowana według nazwisk') ; 'W' : writeln('Lista posortowana według wieku') ; 'P' : writeln('Lista posortowana według płci') ; else writeln('Lista nieposortowana') ; end ; {sortowanie } for i := 2 to ilosc_osob do begin n := i ; repeat case znak of 'I' : zamienic := lista_osob[ n ].imie< lista_osob[ n –1 ].imie ; 11. Pliki 'N' : zamienic := lista_osob[ n ].nazwisko< lista_osob[n –1].nazwisko ; 'W' : zamienic := lista_osob[ n ].wiek < lista_osob[ n–1 ].wiek ; 'P' : zamienic := lista_osob[ n ].plec < lista_osob[ n –1 ].plec ; end ; if zamienic then begin pom := lista_osob[ n ] ; lista_osob[ n ] := lista_osob[ n –1 ] ; lista_osob[ n -1 ] := pom ; end ; dec(n) ; {procedura zmniejszająca n o 1} until (n = 1) or (not zamienic) ; end ; {koniec sortowania} { wyświetlenie listy } for i := 1 to ilosc_osob do begin with lista_osob[i] do begin write(i:2, imie:22, nazwisko:17, ' lat ', wiek :5) ; if plec= k then writeln('Kobieta':12) else writeln(' MęŜczyzna':12) ; writeln('Adres: ', adres:40) end end ; writeln ; write('Wyświetlić powtórnie [ t/n ]: ') ; readln(znak) ; until upcase(znak) = 'N' ; {koniec pętli róŜnego sposobu sortowania} end . 53 54 12. Algorytmy rekurencyjne 12. Algorytmy rekurencyjne Wiele problemów obliczeniowych moŜna zdefiniować rekurencyjnie. Rekurencja oznacza takie zdefiniowanie zagadnienia, gdzie w trakcie formułowania definicji odwołujemy się do niej samej. Przykładem definicji rekurencyjnej moŜe być zapis całkowitej, nieujemnej potęgi rzędu n liczby rzeczywistej x: xn-1*x dla n > 0 1 dla n = 0 (tu uŜycie definiowanej potęgi) n x = Rekurencja w językach programowania jest realizowana za pomocą podprogramów wywołujących kolejno same siebie ze zmienianymi parametrami wywołania. Aby podprogramy rekurencyjne działały poprawnie powinny zawierać warunek zakończenia rekurencji aby wywołanie wykonywane było skończoną liczbę razy. Rekurencja daje proste programy lecz ma takŜe wadę: kaŜde wywołanie podprogramu wymaga wykonania przez procesor dodatkowych czynności, co spowalnia działanie programu oraz powoduje odłoŜenie na stos systemowy duŜej liczby danych. Przykłady: program p12_1; {wyznaczanie całkowitej nieujemnej n-tej potęgi liczby x metodą rekurencyjną} function potega_n (x:real ; n:integer):real ; { funkcja wyznacza potęgę n dla liczby x } begin if n=0 then potega_n := 1 else potega_n := potega_n (x, n – 1)*x ; {w definicji funkcji wykorzystanie tejŜe funkcji} end ; begin {program główny} 10 writeln('2^10=', potega_n (2,10):0:0) ; {obliczenie 2 } end . Kolejny przykład to wyznaczanie metodą rekurencyjną silni liczby x: program p12_2 ; var alfa : integer ; function silnia_x (x: integer): longint ; {zastosowano typ longint ze względu na duŜe wartości funkcji silnia} begin 12. Algorytmy rekurencyjne if x = 1 then silnia_x := 1 else silnia_x := silnia_x (x-1)*x ; end ; begin write ('Podaj liczbę całkowitą : ') ; readln(alfa) ; writeln ('Silnia z liczby ', alfa:5, '=' , silnia_x(alfa):10) ; readln end . 55 56 14. Typ zbiorowy 13. Typ zbiorowy Typ zbiorowy to zbiór potęgowy danego typu porządkowego, czyli zbiór wszystkich podzbiorów tego typu. Zmienna tego typu moŜe zatem zawierać zbiór pusty, jedno- lub wieloelementowy. Definicja typu zbiorowego: type nazwa_typu = set of typ_porządkowy ; ⇑ ⇑ typ zbiorowy typ bazowy Liczba elementów nie moŜe przekraczać 256 – czyli typ bazowy moŜe być tylko typem Byte, okrojonym całkowitym, znakowym (w tym okrojonym) wyliczeniowym i logicznym. Zmienna typu zbiorowego zawierać moŜe dowolny podzbiór elementów typu bazowego, od zbioru pustego do zbioru zawierającego wszystkie elementy. Przykłady definicji typu zbiorowego: type dni = set of (pon,wto,sro,czw,pia,sob,nie) ; znaki = set of 'a'..'z' ; miesiac = (sty,lut,mar,kwi,maj,cze,lip,sie,wrz,paz,lis,gru) ; zbior_miesiecy = set of miesiac ; var nazwa_miesiaca : miesiac ; zbior_nazw : zbior_miesiecy ; UWAGA: nazwa_miesiaca moŜe przyjąć wartość tylko jednej z nazw; zbior_nazw moŜe przyjąć wartość dowolnego podzbioru z nazw bazowych. Zmiennych typu zbiorowego nie wolno uŜywać w instrukcjach czytania i wyprowadzania wyników, uŜywa się ich jedynie w operacjach wykonawczych, testujących itp. Operacje logiczne wykonywane na zbiorach to relacje (porównania): a=b a<>b a <= b a >= b c in a równość zbiorów, te same elementy w obu zbiorach, róŜność zbiorów, róŜne elementy w obu zbiorach (chociaŜ niektóre mogą się powtarzać) zawieranie zbioru a w zbiorze b (true jeśli kaŜdy element zbioru a jest w zbiorze b) zawieranie zbioru b w zbiorze a (true jeśli kaŜdy element zbioru b jest w zbiorze a) czy element c jest w zbiorze a. 14. Typ zbiorowy 57 Wartości stałych typu zbiorowego zapisujemy w nawiasach kwadratowych w postaciach: [lista wartości podzbioru] [okrojenie ze zbioru] lub łącznie: np. [1, 5, 7], np. [1..3], np. [1..3, 5, 7], jak na przykład w programie: program p14_1 ; type x=(alfa, beta, gamma, delta) ; {typ wyliczeniowy} var z : set of x ; begin z := [alfa, beta] ; {lista - wybór podzbioru alfa, beta} z := [alfa..gamma] ; {okrojenie - wybór podzbioru alfa, beta, gamma} end. Inny przykład: program p14_2 ; type miesiace = set of 1..12 ; var v, x, y, z : miesiace ; lit : char ; begin x :=[1..12] ; {cały rok} y := [1..6] ; {pierwsze półrocze} z := [1, 3, 5 , 7 , 8, 10, 12] ; {miesiące posiadające 31 dni} lit := 'a' ; if x< >y then writeln ('tak') else writeln ('nie') ; if x< =y then writeln ('tak') else writeln ('nie') ; if x> =y then writeln ('tak') else writeln('nie') ; if x = y then writeln ('tak') else writeln ('nie') ; if 1 in x then writeln ('tak') else writeln ('nie') ; if 9 in z then writeln ('tak') else writeln ('nie') ; if lit in ['a'..'z'] then writeln ('tak') else writeln ('nie') ; readln end . {tak} {nie} {tak} {nie} {tak} {nie} {tak} Operatory działań na zmiennych typu zbiorowego (znaczenie jak w teorii mnogości): + – ∗ suma zbiorów róŜnica zbiorów iloczyn zbiorów 58 14. Typ zbiorowy Przykład: program p15_3 ; type zbiorowy = set of 1..6 ; var x , y, v : zbiorowy ; begin x :=[2, 3, 4] + [4, 5, 6] ; y := [2, 3, 4] - [4, 5, 6] ; v := [2, 3, 4] * [4, 5, 6] ; y := v * x ; end . {wynik [2,3,4,5,6] } {wynik [2,3] } {wynik [4] (część wspólna) } { przeanalizować wynik}