PASCAL

Materiały pomocnicze do Laboratoriów z Programowania

© Copyright 2000-2002 by Jakub Swacha

 

Jeżeli szukasz Kompendium Pascala jest ono TUTAJ.

Jeżeli szukasz porównania Pascal a C jest ono TUTAJ.

Jeżeli szukasz odpowiedzi na Często zadawane pytania ßkliknij.

Spis treści

 

Spis treści

Wstęp

Uruchamianie

Edycja

Kompilacja

Wykonanie

Struktura programu w Pascalu

Nagłówek programu w Pascalu

Deklaracje

Dane w programie

Stałe

Definicje stałych

Zmienne

Deklaracje zmiennych

Typy złożone zmiennych

Typ tablicowy

Ciągi znaków

Zbiory

Rekordy

Definiowanie własnych typów danych

Lista instrukcji programu

Instrukcja przypisania

Procedury i funkcje

Operatory arytmetyczne

Konwersja typów liczbowych

Porównania w Pascalu

Operatory logiczne

Arytmetyka bitowa

Działania na zbiorach

Hierarchia operatorów

Podstawowe operacje wejścia – wyjścia

Bloki instrukcji

Wybór jednokrotny

Wybór wielokrotny

Trzy rodzaje pętli

Obsługa plików

Deklaracje własnych funkcji i procedur

Wykorzystanie standardowych bibliotek

Pisanie własnych modułów

Testowanie programu

Często zadawane pytania

Literatura

 

Wstęp

 

Poniższy tekst zawiera podstawową wiedzę niezbędną do tworzenia w Pascalu programów o prostym i średnim stopniu skomplikowania. Starałem się wyłożyć tę wiedze w sposób możliwie jasny i oszczędny, co oznacza, że poniższego tekstu nie należy w żadnym razie traktować jako podręcznika, ale raczej jako zestawienie podstaw wiedzy o Pascalu. W oparciu o nie można weryfikować stan posiadanej przez siebie wiedzy, a także wykorzystywać jako repetytorium, by przypomnieć sobie zasady, co do których nasuwają się wątpliwości. Ujęta tu wiedza ułożona jest w kolejności od spraw podstawowych do najbardziej zaawansowanych (co nie musi oznaczać od prostych do trudnych). Wszelkie uwagi co do treści, bądź dostrzeżonych pomyłek, merytorycznych lub technicznych, proszę śmiało zgłaszać na mój e-mail korzystając z linku umieszczonego na samym końcu dokumentu.

Lista literatury, zawierająca obok podręczników Pascala zbiory ćwiczeń oraz pozycje traktujące ogólnie o programowaniu znajduje się przed końcem dokumentu.

 

Uruchamianie

 

Do nauki wykorzystamy dialekt Pascala występujący w pakiecie Turbo Pascal 7.0 firmy Borland.

 

Aby uruchomić ten pakiet, należy zalogować się do sieci, podając login w postaci jak na nalepce na komputerze i hasło stud, a następnie wpisać ntp70.

 

Pakiet Turbo Pascal 7.0 do użytku domowego jest dostępny w taniej wersji edukacyjnej.

W przypadku problemów z działaniem pakietu Turbo Pascal 7.0 na nowszych komputerach, należy zaopatrzyć się w poprawkę dostępną w Internecie.

 

Edycja

 

Edycja polega na wpisaniu tekstu programu do komputera. W wyniku tej fazy otrzymujemy program źródłowy, czyli zapis sposobu rozwiązania zdefiniowanego problemu przy użyciu konwencji Pascala. Oczywiście, edycja nie będzie możliwa póki programista nie będzie miał jasnej koncepcji rozwiązania problemu. Dobry programista koncepcję tę wypracować powinien zanim przystąpi do edycji, a przechodząc od problemu do jego rozwiązania może korzystać z etapów przejściowych, takich jak np. wyrażenie algorytmu w pseudokodzie lub rozrysowanie schematu blokowego. Poprzedzający programowanie etap planowania zwykle przerasta je czasochłonnością.

 

Aby nie tracić bezpowrotnie pracy włożonej w powstanie programu źródłowego, należy zapisywać go w pliku na dysku. Pliki, które zawierają w sobie teksty programów w Pascalu oznaczamy rozszerzeniem PAS. Proste programy mieszczą się z reguły w pojedynczym pliku, skomplikowane muszą być rozdzielone na większą, czasem bardzo dużą, ich liczbę.

 

Plik PAS ze zwykłym plikiem tekstowym różni zawartość, lecz nie sposób zapisu. A zatem, pliki takie można edytować w dowolnym edytorze tekstowym, na przykład tym wbudowanym w nakładkę Norton Commander.

Pakiet Turbo Pascal 7.0 posiada wbudowany edytor, który staje do dyspozycji programisty zaraz po uruchomieniu pakietu – jest to główne okno widoczne na ekranie monitora. Edytor oferuje szereg funkcji usprawniających edycję tekstu dostępnych z rozwijanego menu lub z klawiatury. Do podstawowych poleceń edytora zaliczyć można:

-         skopiowanie zaznaczonego bloku tekstu do schowka Copy (CTRL-INSERT); blok zaznaczamy używając klawiszy strzałek przy wciśniętym przycisku SHIFT

-         skasowanie zaznaczonego bloku tekstu (SHIFT-DELETE)

-         wklejenie tekstu ze schowka w miejsce stania kursora Paste (SHIFT-INSERT)

-         zapisanie programu na dysku Save (F2)

-         odtworzenie z dysku wcześniej zapisanego pliku Open (F3)

-         przechodzenie pomiędzy otwartymi oknami (ALT-numer_okna, np. ALT-1)

-         wyjście z programu Exit (ALT-X)

 

Kompilacja

 

Kompilacja to proces tłumaczenia programu z Pascala, który jest językiem uniwersalnym, na kod maszynowy, to jest język zrozumiały dla konkretnego procesora i systemu operacyjnego na którym ma działać program.

Kompilator na początku swej pracy sprawdza czy w programie nie występują błędy składniowe, a jeśli takowe znajdzie informuje o tym programistę, przerywając dalszą pracę. Błędy kompilacji są zwykle przedstawiane programiście wraz z krótką informacją w języku angielskim, objaśniającą czego dotyczył błąd i gdzie wystąpił. Pozwala to na szybkie znalezienie i usunięcie przyczyny błędu kompilacji.

Jeśli błędów nie ma, program jest kompilowany i konsolidowany do postaci wykonywalnej, która zapisywana jest w postaci pliku z rozszerzeniem EXE. Raz skompilowany program może służyć wiele lat i nie potrzebuje do działania programu źródłowego. Program źródłowy warto jednak zachować by móc później zmieniać program wedle swoich potrzeb. Zmiany w programie źródłowym nie są automatycznie odzwierciedlane w pliku wykonywalnym, aby mieć z nich pożytek należy program źródłowy ponownie skompilować.

 

Pakiet Pakiet Turbo Pascal 7.0 pozwala na kompilację programu źródłowego z poziomu edytora. Aby tego dokonać należy wcisnąć ALT-F9 lub wybrać z menu opcję Compile.

 

Wykonanie

 

Skompilowany program wynikowy może być uruchomiony z wiersza poleceń systemu operacyjnego przez podanie jego nazwy. Czasami program może niespodziewanie przerwać działanie i wyświetlić komunikat błędu. Są to tzw. błędy wykonania. Są one niewykrywalne na etapie kompilacji, jednak w momencie uruchomienia uniemożliwiają pracę programu. W wyszukiwaniu tych błędów programista skazany jest na samego siebie.

Czasami może się zdarzyć sytuacja odwrotna – program nie zakończy się niespodziewanie, przeciwnie – w ogóle nie będzie chciał się skończyć. Może to być wynikiem wewnętrznego zapętlenia się programu - co znaczy że został on źle napisany - albo zupełnego zawieszenia się systemu - co znaczy że program został bardzo źle napisany; oczywiście zdarzyć się też może że winny jest nie program, ale system operacyjny lub sprzęt, jednak nigdy nie powinno to stanowić dla programisty wymówki. W przypadku zapętlenia można próbować przerwać program kombinacją klawiszy CONTROL-C lub CONTROL-BREAK, co jest możliwe zawsze, jeśli programista nie zabezpieczył się przed taką ewentualnością. W przypadku zawieszenia, należy wcisnąć przycisk RESET na obudowie, bo nic innego nie da się już zrobić.

Nie wszystkie popełnione w programie omyłki są na tyle groźne by generować błędy wykonania lub prowadzić do zapętlenia. Zdarza się, że program działa poprawnie, ale nie daje wyników spodziewanych przez programistę, np. zamiast sumy oblicza różnicę itp. Aby być pewnym, że w programie nie ma tego typu pomyłek należy wykonać co najmniej kilka uruchomień próbnych, w których program zostanie wszechstronnie przetestowany na okoliczność podawania błędnych wyników. Programista nigdy nie może mieć pewności, że jego program jest poprawny, lecz z każdą kolejną próbą prawdopodobieństwo przeoczenia jakiejś omyłki maleje.

 

Pakiet Turbo Pascal 7.0 pozwala na wykonanie programu źródłowego z poziomu edytora. Aby tego dokonać należy wcisnąć kombinację klawiszy CONTROL-F9 lub wybrać z menu opcję Run.

 

 

Struktura programu w Pascalu

 

Program w Pascalu powinien zawierać trzy części:

 

-         nagłówek programu, przede wszystkim informujący jak nazywa się program

-         deklaracje, gdzie znajduje się opis typów i nazw danych oraz procedur wykorzystywanych w programie

-         instrukcje, gdzie znajduje się uporządkowana lista czynności do wykonania

 

Jakikolwiek tekst znajdujący się w pliku z programem w Pascalu musi podlegać zasadom składni tego języka. Wprowadzenie dowolnego tekstu umożliwiają nawiasy klamrowe { }. Tekst znajdujący się pomiędzy „{„ a pierwszym napotkanym „}” traktowany jest jako komentarz. Nawiasy klamrowe mogą być zastąpione kombinacją nawiasu okrągłego i gwiazdki (* *), jednak należy pamiętać o tym,  że komentarz otwarty nawiasem klamrowym musi kończyć się nim także, tak samo jest z nawiasami okrągłymi.

 

Uwaga! Od tej pory w tekście zaczną pojawiać się przykłady. Dla uproszczenia, wyrazy pisane w nich dużymi literami są elementami (w większości słowami zastrzeżonymi)  języka Pascal, wyrazy pisane małymi literami to przykładowe nazwy nadane przez autora, w taki sposób by jak najbardziej wyjaśniały rolę którą pełnią.

 

Nagłówek programu w Pascalu

 

W pierwszej linii programu w Pascalu znajduje się informacja identyfikująca. Z reguły ma ona postać:

 

PROGRAM nazwa_programu;

 

Słowo PROGRAM oznacza, że mamy do czynienia z programem który nazywa się nazwa_programu.

Nazwa_programu to przykład identyfikatora, czyli nazwy nadanej przez programistę jakiemuś elementowi jego programu, po to, by móc później się do niego odwoływać. Identyfikatory w Pascalu są jednorazowego użytku, tzn., że jeśli nazwiemy coś x to nazwa ta będzie dla tego czegoś zastrzeżona i nie będzie można nazwać czegoś innego x. A zatem, nic w programie nie może się nazywać tak jak cały program.

Proszę zauważyć, że między słowami „nazwa” a „programu” nie znajduje się spacja, lecz znak podkreślenia (SHIFT-MINUS). Wynika to stąd, że użycie spacji w nazwie identyfikatora jest zabronione. Można używać za to liter, liczb (ale nie na pierwszej pozycji!) i znaku podkreślenia, który używany jest w miejsce spacji.

 

Proszę zwrócić także uwagę na średnik na końcu linii. Średnik kończy prawie każdą linię programu w Pascalu.

 

Deklaracje

 

Część deklaracyjna programu jest opcjonalna, to znaczy, że trywialny program może jej nie mieć w ogóle, a wszystkie jej elementy posiadał będzie jedynie program wysoce rozbudowany. Zawierać może:

-         definicje typów danych i stałych,

-         deklaracje danych,

-         deklaracje funkcji i procedur.

 

Deklaracje można porównać do listy narzędzi i materiałów, które zostały przygotowane do użycia na placu budowy; lista nic nie mówi o sposobie użycia jej elementów, z drugiej strony nie można używać niczego czego nie ma na liście. Zadeklarowanie czegokolwiek nie spowoduje wykonania przez program jakiejkolwiek czynności, z drugiej strony w programie nie wolno używać identyfikatorów, które nie zostały wcześniej zadeklarowane.

 

 

Dane w programie

 

Prawie każdy program wykonujący cokolwiek praktycznego, musi wykorzystywać dane. Dane podzielić można na dwie grupy: dane wejściowe, które muszą być znane aby algorytm mógł dostarczyć rozwiązanie zadania, oraz dane wyjściowe, czyli rezultat działania algorytmu.

Dane wejściowe mogą być umieszczone na stałe w programie, albo wczytywane od użytkownika poprzez urządzenia wejścia za każdym razem, kiedy program dokonuje rozwiązywania problemu. Drugi sposób jest o tyle lepszy, że bez potrzeby zmian w programie źródłowym, pozwala przy pomocy jednego programu na rozwiązywanie nie jednego konkretnego problemu, ale całej ich rodziny.

Warto przy tym zauważyć, że część danych wejściowych ma charakter parametrów algorytmu, które zmieniają się w każdym konkretnym przypadku, druga część to niezmienne wartości wykorzystywane przez algorytm za każdym razem. Na przykład we wzorze na obwód kwadratu, który wynosi długość boku kwadratu razy 4, długość boku jest pierwszego rodzaju, gdyż zmienia się w zależności od wielkości kwadratu, natomiast 4 jest rodzaju drugiego, gdyż we wzorze na obwód zawsze wynosi 4 niezależnie od wielkości kwadratu. Pierwszy rodzaj danych (np.: długość boku) nazywamy zmiennymi, drugi (np. 4) nazywamy stałymi.

Proszę zauważyć, że do długości boku możemy odwoływać się wyłącznie poprzez nazwę (identyfikator). Tak jest ze wszystkimi zmiennymi. Gdybyśmy zastąpili „długość boku” liczbą 5, długość boku przestała by być zmienną a stałaby się stałą. W efekcie otrzymalibyśmy algorytm, który wyliczałby nie obwód dowolnego kwadratu, ale wyłącznie obwód kwadratu o długości boku równej 5.

Stałe

 

Aby w programie użyć stałą wystarczy posłużyć się jej wartością. W Pascalu wielką wagę przykładamy do typów danych, na przykład rozróżniamy liczby i tekst.

W przypadku liczb po prostu je podajemy, np.: 7, 10, lub -0.15. Liczby te są w układzie dziesiętnym, gdybyśmy chcieli użyć zapisu szesnastkowego musimy poprzedzić takie liczby znakiem dolara ($, SHIFT-4), np. liczbę (dziesiętną) 16 możemy zapisać jako $10.

Stałe tekstowe to krótszy lub dłuższy ciąg liter i innych znaków. Jeśliby nie wyróżniać ich w żaden sposób, nie można by poznać co jest stałą tekstową, a co tekstem programu. Aby odróżnić stałe tekstowe od identyfikatorów czy słów zastrzeżonych Pascala, stałe te ujmuje się w apostrofy ( - klawisz na prawo od średnika). Stałe tekstowe muszą zaczynać się i kończyć apostrofem; stała tekstowa musi mieścić się w jednej linii. Jeżeli potrzebny jest dłuższy tekst, musimy rozbić go na kilka części, z których każda zmieści się w jednej linii.

Aby było możliwe zapisywanie znaków w pamięci komputera, każdemu znakowi przyporządkowano jednoznacznie go identyfikujący ciąg bitów. Liczba będąca wartością tego ciągu nosi nazwę kodu ASCII tego znaku. Znając kod ASCII znaku możemy uzyskać znak pod którym się on kryje. W Pascalu, poprzedzenie liczby znakiem kratki (#, SHIFT-3, ang. sharp) oznacza że ma ona być traktowana nie jako liczba, ale jako kod ASCII znaku. Na przykład #65 oznacza dużą literę A. Tego rozwiązania używamy zwykle w przypadku znaków których nie moglibyśmy ująć w apostrofy, np. znaku końca linii który oznaczany jest kombinacją #13#10.

Jeżeli programista potrzebuje stałej tekstowej, w której znajduje się apostrof, może posłużyć się jego kodem ASCII (#39) lub użyć dwóch apostrofów następujących po sobie (‘’). W tym drugim przypadku, np. stała tekstowa oznaczająca apostrof będzie miała postać czterech apostrofów (‘’’’): otwierającego, dwóch oznaczających znak apostrof  oraz zamykającego.

Definicje stałych

 

Zamiast posługiwać się samymi wartościami stałych, można wprowadzić identyfikatory, które będą takie wartości oznaczały. Otrzymamy w ten sposób jak gdyby odpowiedniki zmiennych, z tą różnicą, że wartości stałych przypisane są raz na zawsze i nie mogą być w trakcie programu zmieniane. Takie identyfikatory noszą nazwę stałych nazwanych. Ich użycie jest szczególnie uzasadnione, gdy jakaś stała występuje w programie wielokrotnie.

 

Stałe nazwane deklarujemy w Pascalu w części deklaracyjnej programu, w bloku, który otwiera słowo CONST (ang. constant – stała). Pod tym słowem powinna znaleźć się lista stałych, każda stała zdefiniowana według schematu:

 

nazwa_stalej = wartosc_stalej;

 

W omawianym wcześniej przypadku obwodu kwadratu wyglądałoby to następująco:

 

CONST

    liczba_bokow_kwadratu = 4;

 

Spacje bo obu stronach znaku „=” nie są wymagane, ale polepszają czytelność zapisu.

 

Proszę zauważyć, że w powyższym przykładzie nie używałem polskich znaków, np. napisałem nazwa_stalej, a nie stałej, bokow, a nie boków. Zrobiłem tak, gdyż nie należy używać polskich znaków w nazwach identyfikatorów. Staramy się zastępować je odpowiadającymi im znakami z alfabetu łacińskiego, pozbawionymi akcentów, np.: l zamiast ł, a zamiast ą albo z zamiast ź czy ż. W żadnym wypadku nie należy dopuszczać się błędów ortograficznych by ominąć tę zasadę, tzn. nie wolno pisać rz zamiast ż, ani on zamiast ą.

Przesunięcie linii pod słowem CONST o dwa znaki w prawo nosi nazwę wcięcia. Wcięcia stosujemy do zobrazowania struktury programu. Bloki programu o wyższym poziomie szczegółowości (hierarchicznie podległe) przesuwamy w prawo poprzez zwiększenie ich lewego marginesu. W ten sposób łatwo dostrzec początek i koniec takich bloków, co znacznie ułatwia zrozumienie programu, kiedy używa się w nim złożonych konstrukcji warunkowych czy pętli. Wielkość wcięcia zależy od programisty, w Pascalu przyjęło się jednak stosowanie niewielkich (w porównaniu do np. C) wcięć (2-3 spacje). Aby dodatkowo wyodrębnić blok programu stosuje się puste linie przed i za blokiem. Choć program z wcięciami czy bez wykona się tak samo, przejrzystość programu z wcięciami będzie dalece wyższa, dlatego należy je stosować.

 

Wartości przypisywane stałym muszą być znane w momencie pisania programu. A zatem, skoro jaka jest długość boku kwadratu dowiemy się dopiero od użytkownika, nie możemy zadeklarować jej jako stałej.

 

Wartość przypisana stałej nie musi być liczbą czy ciągiem znaków. Może być także wyrażeniem algebraicznym odwołującym się do innych liczb, czy wcześniej zadeklarowanych stałych. Patrz przykład:

 

CONST

    liczba_bokow_kwadratu = 4;

    boki_dwoch_kwadratow = liczba_bokow_kwadratu+ liczba_bokow_kwadratu;

    powitanie = ‘Hello !’;

    koniec_linii = #13#10;

 

 

Proszę zwrócić uwagę, że w powyższym przykładzie każda linia oprócz linii ze słowem CONST kończy się średnikiem. Po słowie CONST średnika nie ma, gdyż słowo to oznacza początek bloku – w tym przypadku bloku definicji stałych. Jest to generalna zasada – po słowach otwierających jakikolwiek blok nigdy nie ma średników.

 

Zmienne

 

Zmienne to wszystkie te dane, które w toku działania programu będą zmieniały swą wartość. Tylko w ten sposób możemy w programie odwoływać się do tych danych, które w momencie pisania programu są niewiadomą. Aby odwołać się do zmiennej, podajemy jej identyfikator. W niektórych językach programowania (BASIC, PERL) każde napotkane w tekście programu słowo, które nie jest częścią języka programowania traktowane jest jako zmienna. W Pascalu jednak jest inaczej – aby móc używać zmiennych trzeba je wcześniej zadeklarować. Użycie w programie niezadeklarowanych wcześniej identyfikatorów zgłaszane jest jako błąd.

 

Deklaracje zmiennych

 

Deklaracje zmiennych umieszczamy w części deklaracyjnej programu, w bloku który otwiera słowo VAR (ang. variable – zmienna). Poniżej słowa VAR znajduje się lista deklaracji, składających się z nazwy zmiennej, dwukropka (SHIFT-średnik) i typu zmiennej, według schematu:

 

nazwa_zmiennej : typ_zmiennej;

 

Gdy mamy kilka zmiennych tego samego typu możemy je zadeklarować w jednej linii, oddzielając identyfikatory przecinkami:

 

zmienna1, zmienna2, zmienna3 : typ_zmiennej;

 

Nazwę zmiennej wymyśla programista, powinien przy tym dążyć jednak do tego, by nazwy te jak najbardziej wyjaśniały rolę, którą zmienna pełni w programie. Zmiennym pomocniczym, używanym w programie w pewnych miejscach tylko dla chwilowego zapamiętania pewnych wartości, nadaje się zwykle nazwy proste, np. składające się z pojedynczych liter alfabetu.

 

Typ zmiennej określa rodzaj danych do przechowywania których zmienna jest powoływana. Zadeklarowanie zmiennej określonego typu, uniemożliwia lub co najmniej utrudnia przechowywanie w niej danych innego typu. Pascal określa szereg typów zmiennych, do najważniejszych z nich zaliczamy:

 

INTEGER – do przechowywania liczb całkowitych; w przypadku TP70 są one z zakresu –32768 do 32767.

REAL – do przechowywania liczb rzeczywistych w postaci zmiennoprzecinkowej.

CHAR – do przechowywania znaków; jedna zmienna typu CHAR może zawierać tylko 1 znak.

BOOLEAN – do przechowywania wartości logicznych; zmienna tego typu może mieć tylko 2 wartości: TRUE (prawdę) lub FALSE (fałsz).

 

W przypadku, gdy chcemy przechowywać w zmiennej dane INTEGER lub CHAR tylko ze ściśle określonego zakresu, możemy taką zmienną zadeklarować poprzez podanie tego zakresu w postaci: początek..koniec, tzn. pierwszy element zakresu, dwie kropki, ostatni element zakresu.

 

Czasami chcemy przechowywać w zmiennych wartości nie będące ani liczbami ani znakami. Pod warunkiem, że stanowią one zbiór skończony, możemy przechowywać je w ogólnym typie wyliczeniowym (którego INTEGER czy CHAR są jakby podtypami). Taką zmienną deklarujemy następująco:

nazwa_zmiennej: (wartosc1, wartosc2, ..., wartosc_ostatnia);

Wyrazy podane w nawiasach nie identyfikują zmiennych czy typów ale stałe wartości. Nie są tekstem, stąd w programie używamy ich bez apostrofów. Warto przy tym pamiętać aby ich nazwy nie kolidowały z istniejącymi już identyfikatorami.

Jako że są to typy wyliczeniowe, można posługiwać się nimi jak CHAR czy INTEGER, tj. tworzyć z ich elementów listy i zakresy.

 

Przykłady deklaracji:

 

VAR

  nr_buta, ocena : INTEGER;

  oprocentowanie : REAL;

  znak : CHAR;

  czy_jest_wtorek : BOOLEAN;

  litera: ‘A’..’Z’;

  kolory: (czarny, czerwony, niebieski);

  smaki: (gorzki, kwasny, slodki);

 

Deklaracja zmiennych nie ma znaczenia czysto umownego. Każdej deklarowanej zmiennej kompilator przeznacza wymagany przez rozmiar jej typu obszar pamięci operacyjnej komputera.

Deklaracja zmiennej nie oznacza nadania jej jakiejkolwiek wartości. Aby mieć pewność, że zmienna ma na początku programu określoną wartość, programista musi nadać ją sam.

 

Typy złożone zmiennych

 

Wymienione wyżej typy to tzw. typy proste: każda zmienna takiego typu może zawierać w sobie tylko pojedynczą daną danego typu. Czasami liczba danych jest znacznie większa, i aby uniknąć potrzeby deklarowania tylu zmiennych ile mamy danych posiłkujemy się typami złożonymi. Typy takie pozwalają na deklarację zmiennych, które mogą zawierać w sobie wiele pojedynczych danych.

 

Typ tablicowy

 

ARRAY – jest to typ tablicowy, który pozwala na zapisanie w zmiennej wielu pojedynczych danych tego samego typu, do każdej z których odwoływać się można poprzez podanie kombinacji nazwy tablicy i ujętego w nawiasy kwadratowe indeksu, czyli numeru pola tablicy, do którego się odwołujemy, np.: tablica [indeks].

 

ARRAY deklarujemy jak poniżej:

 

nazwa_tablicy : ARRAY [wymiar] OF typ_elementu;

 

Nazwa tablicy to identyfikator pod którym rozpoznawana będzie tablica, typ elementu to typ każdego z pól tablicy, natomiast wymiar to typ indeksu tablicy. Z uwagi na to, że z reguły używamy tablic o bardzo ograniczonych rozmiarach, wymiar ma najczęściej postać zakresu:

 

nr_pierwszego_elementu..nr_ostatniego_elementu

 

Nr pierwszego elementu to wartość indeksu, która wskazuje na pierwszy element tablicy. Nr ostatniego elementu to wartość indeksu, która wskazuje na ostatni element tablicy. Oba numery oddzielają dwie kropki. Przykłady:

 

kody_liter : ARRAY [‘A’..’Z’] OF INTEGER;

dzienne_dochody : ARRAY [1..31] OF REAL;

czy_to_ten_miesiac : ARRAY [1..12] OF BOOLEAN;

 

Tablice nie muszą być jednowymiarowe. Dodatkowe wymiary oddzielamy w deklaracji przecinkami:

 

nazwa_tablicy : ARRAY [wymiar1, wymiar2] OF typ_elementu;

 

Np. deklaracja szachownicy:

 

szachownica : ARRAY [’A’..’H’, 1..8] OF INTEGER;

 

Pojedynczy element tablicy sam może być typu złożonego, np.:

 

dni_roku : ARRAY [1..12] OF ARRAY [1..31] OF INTEGER;

 

Powyższą tablicę można by oczywiście zadeklarować również tak:

 

dni_roku : ARRAY [1..12, 1..31] OF INTEGER;

 

 

Wielkość tablicy określona w wymiarze nie może być modyfikowana w trakcie działania programu. Należy zatem dobierać ją tak, by nigdy nie mogła być przekroczona. Znaczy to, że nie wszystkie pola tablicy muszą być wykorzystane, np. w tablicy dni roku z 31 pól przeznaczonych na dni lutego wykorzystanych będzie tylko 28. Gdyby jednak na każdy z miesięcy przeznaczyć w tej tablicy tylko 28 pól, pojawił by się problem z niemożnością zapisu 29, 30 i 31 dnia innych niż luty miesięcy. Z drugiej strony nie należy przesadzać z nadmiarowością wymiaru tablicy, gdyż deklaracja tablicy pociąga za sobą przeznaczenie dla niej odpowiedniego obszaru pamięci operacyjnej komputera. Zbyt wielkie tablice mogą doprowadzić do przedwczesnego wykorzystania całych zasobów pamięciowych komputera.

 

Ciągi znaków

 

Do zapisu ciągów tekstowych używamy zmiennych typu STRING. Zamiast pisać

 

tekst : ARRAY [1..255] OF CHAR;

 

piszemy :

 

tekst : STRING;

 

(Przy czym uwaga: typ STRING nie jest kompatybilny z typem ARRAY OF CHAR!)

 

Standardowa (i jednocześnie maksymalna) długość 255 może okazać się zbyt wielka. Możemy ją zmniejszyć przy pomocy zapisu:

 

krotki_tekst : STRING [dlugosc_tekstu];

 

gdzie długość tekstu to maksymalna ilość znaków pamiętanych w zmiennej krotki_tekst.

 

Przykłady deklaracji:

 

imie : STRING [20];

nazwisko_i_imie : STRING [50];

napis : STRING;

lista_stu_imion : ARRAY [1..100] OF STRING[20];

 

Aby uzyskać dostęp do poszczególnych znaków ciągu, odwołujemy się do nich jak do elementów zwykłej tablicy, tj. przez podanie indeksu, np. imie[5] zwróci piątą literę imienia.

Warto więc zauważyć, że STRING może być interpretowany jako typ złożony (ciąg znaków) lub prosty (pojedynczy napis).

Zbiory

Do zapisu zbiorów używamy typu SET. Zmienne przechowywujące zbiory deklarujemy jak poniżej:

 

nazwa_zbioru: SET OF typ_pojedynczego_elementu;

 

Przykłady deklaracji:

 

znaki: SET OF CHAR;

litery: SET OF 'a'..'z';

kolory: SET OF (Czerwony, Niebieski, Zielony);

 

Lista elementów zbioru ujęta w nawiasy okrągłe oznacza typ zbioru, czyli definiuje elementy, które mogą do zbioru należeć. Lista elementów zbioru ujęta w nawiasy kwadratowe oznacza konkretny zbiór, czyli określa elementy, które w danej chwili należą do zbioru. Konkretny zbiór musi być podzbiorem zbioru podanego jako typ zmiennej. Na przykład, by zadeklarowana wyżej zmienna kolory zawierała elementy Niebieski i Zielony, podstawiamy je do niej przy użyciu zapisu: [Niebieski, Zielony] (o zasadach podstawiania wartości do zmiennych czytaj dalej).

Należy pamiętać, że elementy zbiorów nie są przechowywane w żadnej ustalonej kolejności, a także, że pojedyncza wartość może być w jednym zbiorze zawarta tylko jeden raz.

Rekordy

 

O ile tablice pozwalają na odwoływanie się do swoich elementów poprzez podanie indeksu (tj. pozycji), a zbiory poprzez podanie wartości, istnieje jeszcze trzeci typ zmiennych złożonych – rekordy, do których elementów odwołujemy się poprzez podanie nazwy. Ponadto, w odróżnieniu od tablic i zbiorów, rekordy mogą przechowywać elementy różnych typów. Elementy rekordów nazywamy polami. Sposób deklaracji rekordu wygląda następująco:

 

nazwa_rekordu: RECORD

  pole1: typ;

  ...

  ostatnie_pole: typ;

END;

 

Przykładowe deklaracje rekordów:

 

kolega: RECORD

  nazwisko: STRING[30];

  imie: STRING[20];

  wiek: INTEGER;

END;

 

pogoda: RECORD

  temperatura: REAL;

  opady: INTEGER;

END;

 

Oczywiście, dozwolone jest tworzenie złożonych typów zhierarchizowanych, np. rekordów zawartych w rekordach:

 

znajomy: RECORD

  nazwisko: STRING[30];

  imie: STRING[20];

  adres: RECORD

    ulica: STRING[30];

    numer: STRING[10];

  END;

END;

 

Najczęstszym przykładem takich praktyk jest implementacja klasycznej tabeli, w której poszczególne wiersze są pozycjami tablicy, a kolumny – polami rekordu:

 

zakupy: ARRAY [1..20] OF

  RECORD

    towar: STRING[30];

    ilość: INTEGER;

    wartość: REAL;

  END;

 

Definiowanie własnych typów danych

 

Aby uczynić kod programu przejrzystszym, krótszym i odporniejszym na pomyłki, programista może pokusić się o zdefiniowanie własnych typów danych. Własne typy tworzymy w części deklaracyjnej programu, w bloku który otwiera słowo TYPE (ang. typ), w oparciu o typy standardowe i uprzednio zdefiniowane.

 

TYPE

  nazwa_typu = definicja_typu;

 

Na przykład:

 

TYPE

  cyfry = 0..9;

  trzy_litery = (A, B, C);

  osoba = RECORD

     imie, nazwisko: STRING [25];

  END;

  osoby = ARRAY [1..100] of osoba;

 

Po deklaracjach powyższych typów, możliwa będzie deklaracja zmiennych o takich właśnie typach. Trzeba przy tym pamiętać, że identyfikatory wykorzystane do nazwania typów, nie będą mogły zostać już użyte do nazwania zmiennych. Dobrze jest zatem do nazw definiowanych typów dostawiać z przodu literę t, tak by odróżnić je od nazw zmiennych, np.:

tCyfry zamiast cyfry, tOsoby zamiast osoby, itd.

 

Lista instrukcji programu

 

Po części deklaracyjnej następuje w kodzie programu lista instrukcji do wykonania. Używane w programie instrukcje można podzielić na wyrażenia proste – mające formę pojedynczych instrukcji przypisania lub wywołań procedur – oraz wyrażenia złożone, do których zaliczamy:

-         bloki

-         wyrażenia warunkowe

-         pętle.

 

Prócz instrukcji Pascala, w kodzie programu występują zdefiniowane wcześniej identyfikatory i stałe nienazwane oraz operatory – czyli znaki lub ich kombinacje wyjaśniające zależności między poszczególnymi częściami wyrażenia (patrz dalej). Poszczególne instrukcje oddzielamy od siebie średnikami ( ; ). Zwykle po średnikach kończymy linie, jednak nie wynika to z zasad języka Pascal, lecz z chęci zachowania przejrzystości programu źródłowego.

Cała lista instrukcji do wykonania ujęta jest w bloku rozpoczynający się słowem BEGIN, a kończącym END i kropką:

 

BEGIN

  instrukcja_1;

  ... (instrukcje) ....

  instrukcja_ostatnia;

END.

 

BEGIN oznacza miejsce od którego program będzie wykonywany, natomiast END. miejsce w którym się kończy. W pliku z programem w Pascalu po „END.” nie może znajdować się już żaden znaczący znak.

Lista instrukcji pomiędzy BEGIN i END. dotyczy tylko programów, tj. kodu zaczynającego się od słowa PROGRAM. Dodatkowe moduły zawierają wywoływane skądinąd funkcje i procedury stąd nie ma w nich wykonywanej listy instrukcji, gdyż nigdy nie są samodzielnie uruchamiane. O budowie takich modułów – czytaj w rozdziale o pisaniu własnych bibliotek.

 

Instrukcja przypisania

 

Instrukcja przypisania służy do nadania zmiennej pewnej wartości. Jej postać:

 

nazwa_zmiennej := wyrazenie;

 

Nazwa_zmiennej jest to zmienna, której przypisujemy wyrażenie. Efektem działania instrukcji przypisania będzie podstawienie zmiennej nazwa_zmiennej wartości wyrażenia. Od momentu wykonania linii programu, w której znajduje się przypisanie, aż do kolejnego przypisania, można przyjmować, że zmienna nazwa_zmiennej ma wartość równą wynikowi wyrażenia.

Połączenie dwukropka i znaku równości oznacza operator przypisania. Operator ten oznacza dla kompilatora informację, co jest do zrobienia: napotkawszy go, wie że powinno zostać wyliczone wyrażenie, a jego wynik przesunięty na pozycję pamięci zajmowaną przez zmienną nazwa_zmiennej.

Wyrażeniem może być tu stała, zmienna, wywołanie funkcji, lub kombinacja tychże połączonych operatorami innymi niż przypisanie. Zmiennej nie jest przypisywane wyrażenie, ale podstawiana jest doń bieżąca wartość wyrażenia. Oznacza to, że zmiana wartości wyrażenia już po podstawieniu go do zmiennej, nie niesie za sobą zmiany wartości tej ostatniej.

Elementy wyrażenia przeliczane są w kolejności od lewej do prawej. Kolejność ta może być zmieniona ze względu na:

-         hierarchię operatorów (patrz dalej)

-         użycie nawiasów (okrągłych).

 

Przykłady podstawień

a := 7;

a := b;

a := c+11;

a := Random(120);

kolor := zielony;

napis := ’zielony’;

znak := #65;

liczba := $20;

 

Procedury i funkcje

 

Drugim (obok przypisania) rodzajem wyrażeń prostych jest wywołanie procedury. Procedury są to wydzielone podprogramy realizujące określone zadania. Wywoływana procedura musi być wcześniej zdefiniowana. Część procedur zdefiniowana jest już w standardzie Pascala (np. procedury realizujące podstawowe operacje wejścia i wyjścia), część zdefiniowana jest w modułach standardowych (np. procedury realizujące obsługę grafiki), inne muszą być zadeklarowane przez samego twórcę programu. Wywołanie procedury wygląda następująco:

 

nazwa_procedury;

 

Często procedury wymagają podania potrzebnych im danych. Dane podawane do procedury w momencie wywołania nazywamy parametrami procedury. Wywołanie procedury z parametrami wygląda następująco:

 

nazwa_procedury (parametr_pierwszy, ..., parametr_ostatni);

 

Należy przy tym pamiętać o zgodności typów parametrów między deklaracją procedury a jej wywołaniem. Np. działanie procedury INC polega na zwiększeniu wartości parametru o jeden (analogiczna procedura DEC realizuje zmniejszenie o jeden). Typem jej parametru jest zmienna typu całkowitego. Próba wywołania tej procedury z parametrem będącym zmienną, która nie jest typu całkowitego, lub jakąkolwiek stałą skończy się błędem kompilacji.

 

Swego rodzaju podgrupą procedur, są takie, których celem jest wyliczenie pojedynczej wartości. Podgrupę tę wyodrębniono i nazwano funkcjami, a wartość wyliczona przez funkcję nazywana jest rezultatem. Wywołanie funkcji polega na podstawieniu jej do zmiennej, która ma przyjąć wartość jej rezultatu:

 

nazwa_zmiennej := nazwa_funkcji (parametry_funkcji);

 

Podobnie jak w przypadku parametrów, należy zawsze pamiętać o wymogu zachowania zgodności typów między rezultatem funkcji, a zmienną do której jest on podstawiany.

 

W odróżnieniu np. od języka C, w Turbo Pascalu funkcje i procedury nie są synonimami, co oznacza, że zabronione jest wywoływanie funkcji jak procedur i odwrotnie. Nowsze wersje TP (np. 7.0) pozwalają na używanie funkcji jak procedur po włączeniu w ustawieniach odpowiedniej opcji. Przykłady prawidłowych wywołań procedur i funkcji:

 

INC (a); {a to zmienna typu INTEGER}

WRITE (‘Wynik wynosi: ‘, wynik);

 

dlugosc := LENGTH (napis);

pierwiastek := SQRT (liczba);

Operatory arytmetyczne

 

Operatory arytmetyczne stosujemy w celu realizacji prostych działań arytmetycznych, w połączeniu ze zmiennymi liczbowymi. Operatory o tym samym wyglądzie mogą być używane także do łączenia zmiennych innych typów, jednak wtedy mają inne działanie.

Do operatorów arytmetycznych zaliczamy: + (dodawanie), - (odejmowanie lub zmiana znaku), * (mnożenie), / (dzielenie bez reszty), DIV (dzielenie z resztą), MOD (reszta z dzielenia).

 

Operatory dodawania, odejmowania i mnożenia stosujemy zarówno do liczb całkowitych jak i rzeczywistych. W przypadku dzielenia sytuacja jest bardziej skomplikowana, a mianowicie:

-         wynikiem operacji dzielenia bez reszty jest liczba rzeczywista, a zatem jej wynik przypisać możemy tylko zmiennej typu rzeczywistego;

-         dzielenie z resztą ma znaczenie tylko dla liczb całkowitych, a zatem operatory DIV i MOD stosujemy wyłącznie w połączeniu z takimi liczbami.

 

Przykłady użycia ( a,b – zmienne typu liczby całkowite; r1, r2 – zmienne typu liczby rzeczywiste ):

a := -a;  {zmiana znaku zmiennej a}

a := b+7;

a := (b+2)*123 mod 17;

r1 := r2+3.01;

r1 := r2 / 1.012;

 

Konwersja typów liczbowych

 

Dane zapisane na zmiennych różnych typów są ze sobą niekompatybilne. Mimo tego, że mogą być interpretowane w ten sam sposób, danych jednego typu nie można przenieść do zmiennych innego typu bez konwersji. Konwersja może występować w programie jawnie,  w postaci wywołań funkcji bądź procedur, lub niejawnie – domyślnie. Drugi przypadek tyczy się na przykład konwersji liczb całkowitych na rzeczywiste, przykładowo poniższy zapis:

 

zmienna_typu_real:= zmienna_typu_integer * 3;

 

jest poprawny, a jego wykonanie będzie obejmowało konwersję z typu integer (parametry) na typ real (wynik). Odwrotny zapis (tj. podstawienie zmiennej typu real do zmiennej typu integer) byłby jednak niepoprawny. Każda liczba całkowita jest liczbą rzeczywistą, ale nie każda liczba rzeczywista jest całkowitą, dlatego nie możemy podstawić liczby rzeczywistej do zmiennej typu integer. Czasami jednak potrzebujemy uzyskać liczbę całkowitą zbliżoną do liczby rzeczywistej – musimy wtedy posłużyć się konwersją jawną. Do zamiany liczb rzeczywistych na liczby całkowite mamy do dyspozycji przede wszystkim dwie funkcje:

 

TRUNC ( zmienna_typu_rzeczywistego )

ROUND (zmienna_typu_rzeczywistego)

 

Różnica między nimi polega na tym, że Trunc konwertując odrzuca część ułamkową, natomiast Round zwraca wartość zaokrągloną z uwzględnieniem części ułamkowej, np.:

 

a := 4.7;  { a – zmienna typu REAL }

b := TRUNC (a);  { b przyjmie wartość 4 }

c := ROUND (a);  { c przyjmie wartość 5 }

 

Porównania w Pascalu

 

Wykonywanie działań arytmetycznych na liczbach to tylko jedna z możliwych do wykonania na nich operacji. Drugą jest porównywanie. W Pascalu do porównywania zmiennych używamy następujących operatorów:

-         równy =

-         większy >

-         mniejszy <

-         większy równy >=

-         mniejszy równy <=

-         nierówny <>

 

Uwaga! Kolejność znaków tworzących operatory dwuznakowe jest nieprzypadkowa!

 

Należy zauważyć, że niezależnie od tego co jest porównywane, rezultatem porównania jest zawsze wartość logiczna, tj. typu BOOLEAN, mogąca mieć wartość prawdy (TRUE) lub fałszu (FALSE). I tak, przykładowo:

4 = 4 ma wartość TRUE,

4 < 4 ma wartość FALSE,

4 >= 4 ma wartość TRUE,

4 <> 4 ma wartość FALSE.

 

Operatory logiczne

 

Wartości logiczne otrzymane w wyniku porównań również mogą być porównywane. Można na nich wykonywać także operacje innego rodzaju, tzw. operacje logiczne. W Pascalu reprezentują je następujące operatory:

-         logiczne zaprzeczenie: NOT

-         logiczne i: AND

-         logiczne lub: OR

-         logiczne albo: XOR

 

Przykładowo:

NOT TRUE ma wartość FALSE

TRUE AND FALSE ma wartość FALSE

TRUE OR FALSE ma wartość TRUE

TRUE XOR TRUE ma wartość FALSE.

 

Arytmetyka bitowa

 

Operatory logiczne mogą być stosowane także dla liczb całkowitych. Operacja logiczna jest wtedy wykonywana na wszystkich bitach wartości połączonej takim operatorem. Do demonstracji powyższego posłużę się zapisem dwójkowym (Uwaga: w Turbo Pascalu nie możemy posługiwać się zapisem dwójkowym liczb!):

 

Liczba dziesiętna 11 w zapisie dwójkowym ma postać %1011

Liczba dziesiętna 12 w zapisie dwójkowym ma postać %1100

 

NOT 11 = NOT %1011 = %0100 { negacja bitowa }

11 AND 12 = %1011 AND %1100 = %1000 { iloczyn bitowy }

11 OR 12 = %1011 OR %1100 = %1110 { suma bitowa }

11 XOR 12 = %1011 XOR %1100 = %0111 { różnica bitowa }

 

Listę operacji arytmetyki bitowej uzupełniają operatory przesuwania bitów: w prawo (SHR) i w lewo (SHL). Na miejsce bitów przesuniętych wstawiane są zera. Przykłady:

 

Przesuń 11 o trzy bity w lewo:

11 SHL 3 = %1011 SHL 3 = %1011000 = 88

Przesuń 12 o dwa bity w prawo:

12 SHR 2 = %1100 SHL 2 = %0011 = 3

 

Działania na zbiorach

 

Zbiory zawierają w sobie nie jedną, lecz wiele liczb. Do oznaczania operacji na nich używamy tych samych znaków, których używaliśmy do operacji na liczbach, w tym wypadku jednak mają one inne znaczenie. Lista dostępnych w Pascalu operacji na zbiorach wygląda następująco:

-         suma +

-         różnica

-         iloczyn *

-         jest podzbiorem <=

-         zawiera podzbiór >=

-         równość (wszystkie elementy obu zbiorów są takie same) =

-         nierówność (nie wszystkie elementy obu zbiorów są takie same) <>

-         należy do IN

 

Załóżmy, że mamy dwa zbiory:

A := [1,2,3,4]

B := [2,3,4]

następujące wyrażenia będą wtedy prawdziwe:

A + B  = [1,2,3,4]

A - B   = [1]

A * B   = [2,3,4]

A <= B = FALSE

A >= B = TRUE

A = B  = FALSE

A <> B = TRUE

1 IN A  = TRUE

 

Hierarchia operatorów

 

Jeżeli wyrażenie zawiera w sobie więcej niż jeden operator, nie są one wykonywane jednocześnie, lecz w ściśle określonej kolejności. Kolejność wyznaczana jest trzema regułami:

1)      w pierwszej kolejności wykonywane są operacje ujęte w nawiasy (okrągłe)

2)      jeśli nawiasów nie ma, o kolejności decyduje hierarchia operatorów (patrz tabelka poniżej)

3)      jeśli obok siebie jest kilka operatorów o tej samej hierarchii, wykonywane są one od lewej do prawej według położenia w wyrażeniu.

 

Operatory

Kolejność wykonania

@ NOT

Pierwsza (najszybciej)

* / DIV MOD AND SHL SHR

Druga

+ - OR XOR

Trzecia

= <> < > <= >= IN

Czwarta (najpóźniej)

 

Na przykład wyrażenie:

 

a:=NOT b – c * 200 MOD ( 14 + 2 )

 

zostanie wykonane w następujących krokach:

 

1)      14+2 = W1

2)      NOT b = W2

3)      c * 200 = W3

4)      W3 MOD W1 = W4

5)      W2 – W4 = W5

6)      a := W5

 

Podstawowe operacje wejścia – wyjścia

 

Podstawowe operacje wejścia-wyjścia to operacje czytania i pisania. W swej najprostszej postaci obsługują one standardowe wejście i wyjście. Standardowe wejście i wyjście jest definiowane przez system operacyjny, a osoba uruchamiająca program może je przekierować, przyjmuje się jednak że standardowe wejście stanowi klawiatura, natomiast wyjście – ekran.

 

Do zapisu danych na standardowym wyjściu używamy procedury WRITE:

 

WRITE ( wyrazenie_do_napisania );

 

Zauważmy, że po nazwie procedury WRITE znajduje się otwarcie nawiasu okrągłego. Oznacza to, że chcemy przekazać procedurze jakiś parametr – parametry zawsze obejmujemy nawiasami okrągłymi. Wyrażenie do napisania może być:

-         stałą, np.: WRITE ( 17 ) lub WRITE ( ‘Dzień dobry!’)

-         zmienną, np.: WRITE ( numer ) lub WRITE ( nazwisko [4] )

-         funkcją, np.: WRITE ( RANDOM ( 6 ) ) lub WRITE ( SQRT ( 8 ) )

-         wyrażeniem zawierającym w sobie wyżej wymienione elementy połączone operatorami, np.: WRITE ( 7+12*3 ) lub WRITE ( 2*rozmiar )

-         listą, zawierającą w sobie kilka wyżej wymienionych elementów rozdzielonych przecinkami, np.: WRITE ( ‘Teraz jest ‘, 17, ‘ godzina ’, minuty, ‘ minut’)

 

Na wyjście zawsze trafia wartość wyrażenia, nie jego nazwa. A zatem, wywołanie procedury z parametrem którym jest stała, a wywołanie jej z parametrem którym jest zmienna, da na ekranie dwa różne efekty, np.:

WRITE ( ‘abc’ ) wyświetli na ekranie napis „abc”,

WRITE ( abc ) wyświetli na ekranie bieżącą wartość zmiennej abc.

 

Jeżeli chcemy by po zakończeniu pisania kursor przeszedł do następnej linii, używamy procedury WRITELN. Jeżeli chcemy nie wyświetlać żadnego tekstu, tylko przejść do następnej linii, piszemy po prostu: WRITELN;

 

Odwrotnością pisania jest czytanie. W przypadku czytania, w chwili pisania programu nie znamy wartości, która ma być wczytana. Znamy natomiast zmienną, do której chcemy podstawić wartość wczytaną z klawiatury i ją to musimy podać jako parametr. Do czytania używamy procedury READ:

 

READ ( zmienna_ ktorej_wartosc_wczytujemy );

 

Na przykład, jeśli chcemy wczytać fragment tekstu, musimy wpierw w części deklaracyjnej zadeklarować zmienną tekstową:

 

VAR

  t: STRING;

 

a następnie w kodzie programu umieścić instrukcję wczytującą:

 

  READ (t);

 

Gdybyśmy zadeklarowali zmienną t jako np. INTEGER, a użytkownik wpisałby znaki inne niż numeryczne, program zgłosiłby błąd wykonywania.

Do potwierdzenia wprowadzenia danych służy klawisz ENTER. Instrukcja READ jednak czyta same dane, wciśnięcie klawisza ENTER pozostawiając w buforze klawiatury – co może sprawić nam później problemy. Aby wczytać dane i oczyścić bufor klawiatury z kończącego je ENTER-a, używamy procedury READLN. Jeżeli chcemy nie wczytywać żadnych danych, tylko zatrzymać program do momentu wciśnięcia przez użytkownika przycisku ENTER, piszemy po prostu: READLN;

 

Bloki instrukcji

 

Przez bloki instrukcji rozumiemy uszeregowane listy instrukcji do wykonania. Wykonanie bloku instrukcji przebiega od góry do dołu, odbywa się za każdym razem i zawsze jednokrotnie, a zatem niczym nie różni się niczym od wykonywania instrukcji niezblokowanych. A jednak istnieją powody dla których używamy bloków instrukcji. Oprócz zalet w postaci polepszonej przejrzystości kodu, bloki umożliwiają wykonywanie kilku zamiast jednej instrukcji prostej w połączeniu z pewnymi wyrażeniami złożonymi.

Blok posiada początek, zawartość i koniec. Zawartość stanowi lista instrukcji do wykonania, początek w Pascalu oznaczamy słowem BEGIN, natomiast koniec – END, po którym nie stawiamy jednak kropki, jak to jest w przypadku END kończącego cały program.

 

Wybór jednokrotny

 

Częstym przypadkiem jest, że pewne części programu, aby działał on prawidłowo, muszą być wykonywane lub nie w zależności od bieżącej sytuacji. Do oznaczenia takich części służy konstrukcja wyboru jednokrotnego. W Pascalu ma ona postać:

 

IF warunek THEN instrukcja;

 

Powyższy zapis oznacza, że „instrukcja” zostanie wykonana wtedy i tylko wtedy, gdy „warunek” będzie spełniony, np.:

 

IF napis = ‘nie’ THEN WRITE (‘No to trudno’);  { wyświetli tekst ‘No to trudno’ jeśli zmienna napis zawiera tekst ‘nie’)

IF a < 0 THEN a = -a; { jeżeli zmienna a miała wartość ujemną, to zamieni ją na dodatnią }

 

Jako że po słowie THEN spodziewana jest tylko jedna instrukcja, chcąc umieścić ich tam więcej koniecznie należy posłużyć się konstrukcją bloku, np.:

 

IF dzien = niedziela THEN

  BEGIN

    WRITELN (‘W niedzielę nie pracuję’);

    EXIT;

  END;

 

Wybór, w którym instrukcje wykonywane są tylko przy spełnieniu warunku nazywamy wyborem prostym. Istnieje także wybór pełny, w którym instrukcje wykonywane są także przy niespełnieniu warunku, przy czym są to oczywiście inne instrukcje, niż w przypadku spełnienia go. W Pascalu wybór pełny zapisujemy następująco:

 

IF warunek THEN instrukcja ELSE inna_ instrukcja;

 

Zwracam uwagę, że przed słowem ELSE nie ma średnika! (podobnie jak przed THEN, ale to ELSE zwykle sprawia studentom kłopoty). Przykład:

 

IF a < 0 THEN

  WRITELN(‘Dodatnia’) { ß tu nie ma średnika! }

ELSE

  WRITELN(‘Ujemna’);

 

Wyrażeniem po THEN lub ELSE może być kolejna konstrukcja IF. Mamy wtedy do czynienia z wyborem zagnieżdżonym. Przykład:

 

IF dzien = niedziela THEN

  BEGIN

    WRITELN (‘W niedzielę nie pracuję’);

    EXIT;

  END

ELSE

  IF dzien = sobota THEN

    IF NOT pracujaca THEN

      BEGIN

        WRITELN (‘W sobotę niepracującą nie pracuję’);

        EXIT;

      END;

 

Powyższy ciąg instrukcji wykona pewne operacje w przypadku gdy zmienna dzien wskazuje na niedzielę, lub wskazuje na sobotę a zmienna pracujaca ma wartość FALSE (zwracam tu uwagę na wyrażenie „NOT pracujaca” które jest równoznaczne wyrażeniu „pracujaca = FALSE”). Widzimy, że już podwójny wybór powoduje spore skomplikowanie struktury programu. Gdybyśmy chcieli wykonywać różne instrukcje dla wszystkich dni tygodnia, łatwo wyobrazić sobie jak duże drzewko IF-ów otrzymalibyśmy. Zgrabniejszą formą sprawdzenia wartości jednej zmiennej jest instrukcja wyboru wielokrotnego.

Wybór wielokrotny

 

W Pascalu instrukcją wyboru wielokrotnego jest CASE:

 

CASE wyrażenie OF

  wartosc1: instrukcja1;

  wartosc2: instrukcja2;

  ...

  wartosc_n: instrukcja_n;

  ELSE inna_instrukcja;

END;

 

Powyższa konstrukcja wykona instrukcja1, jeśli zmienna jest równa wartosc1, instrukcja2 jesli zmienna wynosi wartosc2 itd., a instrukcja_inne zostanie wykonane jeżeli wartość zmiennej jest inna od wszystkich wartości wypisanych w bloku CASE.

O czym należy pamiętać stosując CASE:

-         nie zapominać o blokach (BEGIN ... END) jeżeli dla danej wartości ma być wykonana więcej niż jedna instrukcja prosta

-         zamiast pojedynczych wartości można stosować listy i zakresy, gdy chcemy wykonywać te same instrukcje dla różnych wartości

-         obecność ELSE w bloku CASE nie jest wymagana; w takim przypadku, gdy wartość zmiennej nie została objęta którymś z przypadków nie stanie się nic

-         przed ELSE w bloku CASE stawiamy średnik

-         nie zapominać o END kończącym blok CASE.

 

Przykład:

 

CASE dzien OF

  niedziela: 

  BEGIN

    WRITELN (‘W niedzielę nie pracuję’);

    EXIT;

  END;

  sobota:

  IF pracujaca THEN

         WRITELN (‘W sobotę pracującą pracuję’)

      ELSE {do IF}

        BEGIN

          WRITELN (‘W sobotę niepracującą nie pracuję’);

          EXIT;

        END;

  ELSE {do CASE}

    WRITELN (‘W dzień powszedni pracuję’);

END;

 

Trzy rodzaje pętli

 

Pętlą nazywamy fragment programu, który wykonywany jest wielokrotnie. Pascal pozwala na tworzenie trzech rodzajów pętli. Najprostszy z nich ma postać:

 

REPEAT

  lista_instrukcji_wewnątrz_pętli;

UNTIL warunek_końca_pętli;

 

Powyższy zapis oznacza, że po wykonaniu listy_instrukcji_wewnątrz_pętli, sprawdzany jest warunek_końca_pętli i jeżeli nie został on spełniony, wykonywanie programu wraca do punktu oznaczonego słowem REPEAT. Oczywisty wniosek jest taki, że tego rodzaju pętla wykonać się musi co najmniej raz. Zdarzyć się może, że potrzebna jest pętla, która sprawdza warunek już na swoim początku, tak by instrukcje w jej wnętrzu, jeśli jest taka potrzeba, nie wykonały się ani razu. W Pascalu zapiszemy to następująco:

 

WHILE warunek_trwania_pętli DO instrukcja;

 

Gdy instrukcji w pętli ma być więcej używamy konstrukcji bloku:

 

WHILE warunek_trwania_pętli DO

  BEGIN

    lista_instrukcji_wewnątrz_pętli;

  END;

 

W wielu sytuacjach już w momencie pisania programu programiście wiadomo jest ile razy ma zostać wykonana pętla. Wtedy najprościej jest użyć pętli typu

 

FOR licznik := wartość_początkowa TO wartość_końcowa DO instrukcja;

 

Powyższy zapis oznacza, że:

1)      zmiennej całkowitej licznik zostanie nadana wartość_początkowa

2)      o ile wartość_początkowa nie jest większa niż wartość_końcowa, zostanie wykonana instrukcja; w przeciwnym razie następuje wyjście z pętli

3)      licznik zwiększana jest o 1

4)      powrót do punktu 2

 

Uwaga! W Pascalu licznik możemy wewnątrz pętli jedynie czytać, zabronione jest zmienianie jego wartości!

 

Jeżeli potrzebujemy zamiast licznika rosnącego – malejący, używamy poniższej formuły:

 

FOR licznik := wartość_początkowa DOWNTO wartość_końcowa DO instrukcja;

 

W takim przypadku, warunkiem, by pętla wykonała się choć raz, jest by wartość_początkowa była nie mniejsza niż wartość_końcowa.

Tak jak w przypadku WHILE pętle zawierające więcej niż jedną instrukcję ujmujemy w BEGIN...END.

Podczas wykonywania instrukcji wewnątrz pętli może zajść potrzeba modyfikacji dalszego biegu programu:

1)      Aby błyskawicznie opuścić pętlę w dowolnym jej miejscu używamy instrukcji BREAK

2)      Aby przejść w dowolnym miejscu pętli do jej następnego obiegu (iteracji) używamy CONTINUE

 

Przykłady użycia pętli z opisem (w komentarzach):

 

REPEAT

  READLN (a);

UNTIL a > 0; {tak długo wczytuj wartość zmiennej A, aż będzie to liczba dodatnia }

 

WHILE a > 256 DO

  a := a DIV 2;  {jak długo A jest większe niż 256, dziel A przez 2 }

 

FOR i := 1 TO LENGTH (napis) DO

  WRITELN ( napis [i] );  { wypisze wszystkie litery zmiennej napis, każdą w jednej linii }

 

Obsługa plików

 

W przypadku, gdy program przelicza duże ilości danych, ograniczenie się do korzystania tylko ze standardowego wejścia/wyjścia powoduje szereg uciążliwości. W takich sytuacjach warto korzystać z plików. Pascal rozróżnia trzy rodzaje plików:

-         pliki tekstowe, deklarowane przy użyciu typu TEXT;

-         pliki o określonym typie, deklarowane przy użyciu składni FILE OF typ;

-         pliki bez określonego typu, deklarowane przy użyciu typu FILE.

 

Aby wykonywać jakiekolwiek operacje na pliku, oprócz zadeklarowania zmiennej plikowej, należy powiązać ją z konkretnym plikiem istniejącym (lub mającym pojawić się) na dysku. Służy do tego procedura ASSIGN.

Przed rozpoczęciem czytania z (lub pisania do) pliku, należy go jeszcze otworzyć. W Pascalu dostępne są trzy służące temu procedury:

-         REWRITE, tworzące na dysku nowy plik (stary, o ile taki był, jest zamazywany);

-         RESET, otwierający istniejący plik do czytania i modyfikacji;

-         APPEND, otwierający istniejący plik do dopisywania.

 

Do czytania i zapisywania do/z plików używamy tych samych procedur, jak przy dostępie do standardowego wejścia/wyjścia, tj. READ i WRITE, z tą różnicą, że jako pierwszy parametr podajemy wykorzystywaną zmienną identyfikującą plik. Przy czytaniu, aby sprawdzić, czy został osiągnięty koniec pliku, korzystamy z funkcji EOF, która zwraca wtedy wartość TRUE. Na zakończenie używania pliku, zamykamy go procedurą CLOSE. Przykład programu, którego zadaniem jest załadowanie pliku tekstowego i wypisanie na standardowym wyjściu:

 

PROGRAM more;

VAR

   plik : TEXT;

   s : STRING;

BEGIN

ASSIGN (plik, ‘a:\nazwa pliku’);

RESET (plik);

WHILE NOT EOF(plik) DO BEGIN

   READLN (plik, s);

   WRITELN (s);

END;

CLOSE (plik);

END.  

Deklaracje własnych funkcji i procedur

Pascal jako język strukturalny pozwala na wyodrębnianie części programu w postaci podprogramów. Własne funkcje i procedury deklarujemy w części deklaracyjnej programu, korzystając z następującej składni:

 

nagłówek;

deklaracje lokalne;

ciało procedury lub funkcji;

 

Z uwagi na to, że w Pascalu dostępne są tylko te identyfikatory, które zostały wcześniej zadeklarowane, w przypadku funkcji/procedur, które wzajemnie się do siebie odwołują, korzystamy z dyrektywy FORWARD:

 

nagłówek; FORWARD;

 

uprzedzającej kompilator o późniejszej deklaracji funkcji/procedury o podanej nazwie i parametrach.

 

Nagłówki procedury i funkcji mają następującą budowę:

PROCEDURE nazwa_procedury (parametry : typy_parametrów);

FUNCTION nazwa_funkcji (parametry : typy_parametrów) : typ_rezultatu;

 

Jako parametry mogą być przekazywane wartości lub zmienne, jak w przykładzie:

 

PROCEDURE zrob_cos1 (x : INTEGER); {do procedury przekazujemy wartość, która nadana zostaje zmiennej x;

zmiana wartości zmiennej x wewnątrz procedury nie zmienia nic na zewnątrz procedury}

PROCEDURE zrob_cos2 (VAR x : INTEGER); {do procedury przekazujemy zmienną, której nadany zostaje alias x;

zmiana wartości zmiennej x wewnątrz procedury zmienia wartość zmiennej na zewnątrz}

 

Ten drugi sposób pozwala na modyfikację wartości zewnętrznych zmiennych przez funkcję/procedurę, co ułatwia dwukierunkową komunikację.

Deklaracje lokalne mogą zawierać deklaracje i definicje tych samych elementów, co globalna część deklaracyjna programu, tj. stałych, zmiennych, typów, a nawet funkcji i procedur. Wszystkie identyfikatory zadeklarowane w części deklaracyjnej funkcji lub procedury (tj. pomiędzy jej nagłówkiem a ciałem) są dostępne wyłącznie wewnątrz funkcji, której dotyczą.

Ciało funkcji powinno zawierać instrukcję zwrócenia wyniku, w postaci:

 

nazwa_funkcji := wynik; {gdzie wynik musi być zgodny z typem funkcji podanym w nagłówku}

Wykorzystanie standardowych bibliotek

 

Wspominałem o programach, które składają się z wielu plików. Aby wskazać, że mamy do czynienia z takim właśnie programem, używamy słowa USES (ang. uses – korzysta), które występuje zaraz poniżej nazwy programu.

 

PROGRAM nazwa_programu;

USES modul;

 

Moduł to właśnie taki dodatkowy plik. W przypadku gdy modułów jest wiele, oddzielamy je przecinkiem, czyli wystarczy napisać:

 

PROGRAM nazwa_programu;

USES modul1, modul2, modul3;

 

Dodatkowe moduły możemy pisać sami – patrz rozdział àPisanie własnych modułów. Z uwagi jednak na to, że istnieje duża grupa czynności, która jest wspólna dla wielu programów, aby uwolnić programistów od konieczności samodzielnego ich programowania, stworzono moduły standardowe. Moduły te zawierają popularne – lecz nie ujęte w standardowym zestawie instrukcji Pascala - funkcje i procedury pogrupowane tematycznie, i dystrybuowane są wraz z Turbo Pascalem. Oznacza to, że by używać funkcji i procedur z danego modułu standardowego wystarczy podać jego nazwę po słowie USES.

Do najczęściej używanych modułów standardowych należą:

1)      CRT zawierający obsługę konsoli (ekran, klawiatura). Obejmuje m.in. takie procedury jak czyszczenie ekranu, ustawianie kolorów, badanie stanu klawiatury. Ze względu na zawarty w tej bibliotece błąd, aby korzystać z programów ją wykorzystujących na nowszych komputerach należy użyć specjalnej poprawki (do pobrania z Internetu).

2)      DOS zawierający obsługę szeregu funkcji systemu operacyjnego (m.in. czas i data systemowa).

3)      GRAPH zawierający obsługę trybu graficznego.

 

Pisanie własnych modułów

Aby odróżnić moduł od właściwego programu, w jego nagłówku umieszczamy zamiast słowa PROGRAM słowo UNIT:

 

UNIT nazwa_modulu;

 

Oczywiście moduły mogą same korzystać z innych modułów:

 

UNIT nazwa_modulu;

USES modul1, modul2, modul3;

 

Dwie najważniejsze części modułu to INTERFACE i IMPLEMENTATION. W części INTERFACE umieszczamy deklaracje tych identyfikatorów oraz nagłówki tych funkcji i procedur, które mają być dostępne dla programów wykorzystujących tworzony moduł. W części IMPLEMENTATION umieszczamy ciała zapowiedzianych w INTERFACE funkcji i procedur, jak również deklaracje wszelkich stałych, zmiennych, funkcji i procedur przeznaczonych tylko do użytku wewnątrz modułu, tj. niewidocznych dla programów wykorzystujących tworzony moduł.

Moduł kończy się tak jak program (END.).

Moduł bez programu jest bezużyteczny, nie da się go uruchomić. Z drugiej strony, jeden moduł może być używany w wielu programach.

 

Testowanie programu

Turbo Pascal 7.0 posiada wbudowany debugger pozwalający na sprawne testowanie i wyszukiwanie błędów w programie. Zestaw jego podstawowych użyteczności obejmuje:

-         tworzenie punktów przerywania wykonywania programu (Breakpoints);

-         wykonywanie programów instrukcja po instrukcji (Step Over, F8);

-         podgląd aktualnych wartości zmiennych i wyrażeń (Watches).

Często zadawane pytania

 

Pytanie: Mój komputer nie działa.

Odpowiedź: Proszę przesiąść się do tego obok.

 

P: Mój program nie chce się skompilować.

O: Zawiera błąd. Typowe błędy:

1)       Nie używamy spacji w nazwach.

2)       Zmienne trzeba deklarować.

3)       Na końcu linii stawiamy średnik.

4)       Przed ELSE nie stawiamy średnika.

5)       Do dzielenia liczb całkowitych używamy DIV a nie slasha.

 

P: Mój program jest dobry i nie chce się skompilować.

O: Prosiłem o uruchomienie Turbo Pascala, nie Turbo C.

 

P: Nie widzę, czy mój program coś robi.

O: Podgląd ekranu użytkownika – ALT-F5.

 

P: Mój program za szybko się kończy.

O: Trzeba postawić na jego końcu (ale przed END.) READLN.

 

P: Mój program nie chce się skończyć

O: Wcisnąć CTRL-C lub CTRL-BREAK. Później ewentualnie ENTER.

 

P: Mój program nie reaguje na CTRL-C i CTRL-BREAK.

O: W Windows wcisnąć ALT-TAB. Jeśli to nie pomaga, lub w DOS wcisnąć RESET.

 

P: Co zrobić, by nie pisać dwa razy tego samego w przypadku jak wyżej?

O: Zapisywać, to co się dotąd napisało (F2), szczególnie przed próbą uruchomienia.

 

P: Mimo wprowadzenia poprawek do programu, cały czas uruchamia się stara wersja.

O: Trzeba program przekompilować. Najpierw ALT-F9, a dopiero potem CTRL-F9.

 

P: Mam tyle otwartych okienek, ale nie widzę swojego programu.

O: ALT-zero – lista otwartych okien.

 

P: Jak pozamykać niepotrzebne okna?

O: ALT-F3 zamyka bieżące.

 

P: Gdzie jest plik zapisany przez mój program?

O: Tam, gdzie wskazuje aktualny katalog. Podawanie pełnych ścieżek pozwoli uniknąć niespodzianek.

Literatura

Podręczniki:

Kubiak Mirosław „Programuję w językach Turbo Pascal i C/C++”

Majczak Adam „Pascal nie tylko dla orłów”

Płużański Tadeusz „Pascal”

Porębski Wiesław „PASCAL Wprowadzenie do programowania”

Struzińska-Walczak Anna, Walczak Krzysztof „Nauka programowania dla początkujących”

Struzińska-Walczak Anna, Walczak Krzysztof „Nauka programowania dla już nie całkiem początkujących”

Struzińska-Walczak Anna, Walczak Krzysztof „Programowanie w języku Turbo Pascal 7.0”

Wirth Nicolas „Algorytmy + struktury danych = programy” ok. 40 zł

Ćwiczenia:

Arciszewski Włodzimierz „Ćwiczenia z Turbo Pascala”

Kierzkowski Andrzej „Turbo Pascal. Ćwiczenia praktyczne”

 

 

 

Uwagi proszę zgłaszać TU