Programski-Jezici-i-Strukture-Podataka

January 20, 2017 | Author: Kalista0101 | Category: N/A
Share Embed Donate


Short Description

Programski-Jezici-i-Strukture-Podataka...

Description

1

Semestar

FAKULTET TEHNIČKIH NAUKA UNIVERZITETA U NOVOM SADU

Elektrotehnika i računarstvo Odsek za računarstvo i automatiku

prof. dr Dušan Malbaški

02 ESE2 501

Programski jezici i

strukture podataka

2

P R O F. D R D U Š A N M A L B A Š K I , T M D 1 4 5

Programski jezici i strukture podataka

UNIVERZITET U NOVOM SADU Fakultet tehničkih nauka Elektrotehnika i računarstvo Odsek za računarstvo i automatiku Šifra predmeta: 02 ESE2 501 Semestar: I Predavanja u semestru/u nedelji: 60 / 4 Vežbi u semestru/u nedelji: 10N+50C / 4 Asistenti: mr Branko Markoski, TMD 9A Žarko Ivanov, TMD 15A LITERATURA: O'Brian S.: Turbo Pascal 6.0, Mikro Knjiga, Beograd, 1991. (ili ekvivalent) Malbaški D., Obradović D.: Osnovne strukture podataka, UNS, 1995. Hotomski P., Malbaški D.: Matematička logika i principi programiranja, UNS, 2000. Malbaški D.: Odabrana poglavlja metoda programiranja, UNS, 2002.

Sadržaj U V O D Hijerarhija operatora

6

Logički tip

7

Celobrojni tip

8

Realni tip

9

Znakovni tip

10

String

11

Enumeracija

11

Intervalni tip

12

Nizovi

13

Skupovi

14

Slog

16

Datoteke

17

Pokazivači

23

Primer

26

Zaglavlje

28

Deklaracije

29

Algoritamski deo

31

Naredba dodele

32

Složena naredba (sekvenca)

32

Naredbe selekcije

33

Petlje

34

Naredbe skoka

36

Moduli

44

Opšta struktura modula

46

Uniti u Turbo Pascalu

47

Pascalovi gotovi uniti

49

Primer

50

Bekus-Naurova forma – BNF

52

Poboljšana Bekus-Naurova forma – EBNF 53 Sintaksni dijagram

54

Šta je to algoritam?

56

Algoritamski sistemi

57

Rekurzivne funkcije

57

Pravilni programi

71

Analiza algoritama

72

Strukturna teorema i programiranje bez goto..........................................................................74 Metoda Aschroft-Manna

81

Pisanje programa bez upotrebe skokova 83 Pojam podatka

86

Značaj struktura podataka

87

Definicija strukture podataka

89

Grafovi

90

Operacije nad strukturama podataka

90

Klasifikacija struktura podataka

93

Klasifikacija operativnih struktura

95

Nizovi

96

Slogovi

101

Tabele

104

Stek (stack)

105

Red (queue)

110

Dek (deque)

114

Sekvenca

115

Liste i dinamički nizovi

119

Stabla

124

1 Tema

Programski jezik Pascal U ovom delu ćemo obraditi osnove programskog jezika Pascal, sa posebnom pažnjom na Turbo Pascal. Bavićemo se tipovima podataka u Turbo Pascalu, a spomenućemo i opštu strukturu pascalskih programa, neke osnovne naredbe i rad sa potprogramima.

P

askal se pojavio 1969. Njegov autor je Švajcarac Niklaus Wirth, a ime je dobio po Francuzu Blezu Pascalu, izumitelju prve mašine za računanje. Nastao je posle prve velike softverske krize koja je nastala zbog toga što tadašnji softver nije mogao da zadovolji potrebe korisnika i iskoristi mogućnosti hardvera. Pascal je nastao na bazi programskog jezika ALGOL (ALGOrhitmic Language) tako što je preuzeta većina komandi (neke su odbačene), a uvedene su nove strukture podataka. Inače, Pascal nije prvobitno pisan za izvršavanje na računaru, već za prikazivanje programa, tj. imao je edukativnu ulogu. Pascal je standardizovan programski jezik što znači da postoji neka osnovna struktura koja je ista za sve verzije ovog programskog jezika i stoga se program pisan za jednu verziju uz manje izmene može izvršavati na računaru sa nekom drugom verzijom Pascala. Zbog toga, osnovna svrha Pascala kakav je on sad je pisanje čitljivih i razumljivih programa. Takav program se lako ispravlja i modifikuje, a takođe ga nije potrebno posebno dokumentovati, jer je sam program prilično

1

razumljiv. Pascal je prvi programski jezik koji ima takvu strukturu da je omogućio pisanje programa bez upotrebe skokova . Četiri aspekta koje treba razjasniti prilikom programiranja

1. Kakvim tipovima podataka raspolažemo? 2. Kakvim raspolažemo?

upravljačkim

strukturama

3. Kako se organizuje ulaz i izlaz? 4. Kako se vrši modularizacija procedura, funkcija i modula)?

2

(upotreba

D E S I G N

C U S T O M I Z A T I O N

Alfabet Pascala Skup simbola koji se koriste u programskom jeziku naziva se alfabet programskog jezika . Pascal je veštački jezik i stoga poseduje definisan skup simobla koji se smeju koristiti.

strogo

U alfabet Pascala spadaju: • slova engleske abecede (karakteri), bez razlike velikih i malih slova i donja crta _ •

cifre od 0 do 9



specijalni znaci (+, =, :, -, *, :=, , >=, = < =, 3,5 then g := sin(x) else if x = 3,5 then g:= 0.0 else g := cos(x)

Case

Naredba case predstavlja naredbu višestruke selekcije. To je izvedena naredba. Koristimo je kada treba da uporedimo vrednost nekog izraza sa unapred zadatim vrednostima. Mora se voditi računa da izraz ne sme biti tipa real i string.

33

P R O G R A M S K I

J E Z I K

P A S C A L

Sintaksa: case izraz of vrednost1_1, vrednost1_2: naredba1; vrednost2_1: naredba2; ... vrednostN: naredbaN; end;

Primer: case i + s of 1, 4: y := sin(z); 0: begin y := 0; j := 1 end; -10: x := x + 1 end;

Petlje

 predstavlja

Petlje predstavljaju jednu od najosnovnijih struktura u programiranju. Pomoću njih se fizički ograničenim brojem naredbi računaru može zadati proizvoljno veliki psao. U Pascalu postoje tri naredbe ciklusa: for, while i repeat. Svaki ciklus jedinstvenu naredbu.

For

For je brojački ciklus. For petlja ima upravljačku promenljivu čija vrednost upravlja ponavljanjem ciklusa. Njena vrednost se menja i proverava automatski, stoga kod ove petlje postoji određena automatizacija. Sintaksa: for kp := pv {to|downto} kv do naredba; Upravljačka

Gde je kp ime kontrolne promenljive, pv je početna vrednost, a kv krajnja vrednost. Koristimo to ili downto zavisno kontr od toga želimo li da se prilikom izvršenja vrednost kontrolne promenljive olna petlje prom odbrojava unapred ili unazad od početne enljiv do krajnje vrednosti (npr. od 1 do 10 ili od a. 10 do 1). prome nljiva se zove i

Ova petlja ne mora da se izvrši ni jednom. Koristimo je kada je u trenutku otpočinjanja ciklusa poznato koliko će biti ponavljanja. 34

P R O G R A M S K I

J E Z I K

P A S C A L

While

Ovo je osnovna vrsta ciklusa, pošto se njim može rešiti bilo koje ponavljanje. Sintaksa: while logicki_izraz do naredba;

fals e

logički izraz tru e

Na BDA naredbe while se vidi da se prvo proverava vrednost logičkog izraza, pa ako je izraz tačan, izvršava se naredba. Zbog toga se ova petlja ne mora

naredba

izvršiti

ni

jednom.

Naravno, broj ponavljanja ne mora biti unapred poznat, ali je bitno da naredba utiče na vrednost logičkog izraza. U suprotnom se petlja ili uopšte neće izvršiti (ako je izraz netačan pre ulaska u petlju) ili će se izvršavati beskonačno (pošto je izraz uvek tačan). Repeat

Sintaksa:

blok naredbi logički izraz tru

fals e

repeat naredba_1; naredba_2; ...; naredba_n until logicki_izraz;

e

Repeat naredba je slična naredbi while. Doduše, mnogo manje se koristi. Za razliku od while, pošto se uslov proverava na kraju, repeat se izvršava makar jednom. Ako je naredba sekvenca, ne koriste se programske zagrade begin i end, jer se ponavlja sve što je između repeat i until. Najvažnija razlika između while i repeat je što se while ponavlja dok iskaz jeste tačan, a repeat dok iskaz ne postane tačan. 35

P R O G R A M S K I

J E Z I K

P A S C A L

Naredbe skoka U Pascalu postoji pet naredbi skoka. Kao što je iznešeno ranije, Pascal je tako koncipiran da je potreba za skokom skoro eliminisana. Osnovna naredba skoka je goto. Njena sintaksa je: goto labela;

Labela je mnemonički identifikator. Naredbe skoka treba izbega vati.

ciklusa; •

continue



exit

Pored ove postoje i naredbe skoka koje se koriste u ciklusima i potprogramima: •

break



momentalni

izlazak

iz

– momentalni skok na početak ciklusa;

– momentalno završavanje potprograma;

• halt – momentalno zaustavljanje programa. Ova naredba je najčešće propraćena porukom za korisnika.

36

P R O G R A M S K I

J E Z I K

P A S C A L

Elementarni ulaz/izlaz Elementarni ulaz jeste ulaz sa tastature, a elementarni izlaz jeste izlaz na ekran. U Pascalu se ovo obavlja pomoću dva potprograma (dve procedure) čiji je broj parametara promenljiv. Za izlaz na ekran u tekstualnom režimu se koristi: write(p1, p2, ..., pN); writeln(p1, p2, ..., pN); writeln;

Gde je pi konstruktor sastavljen na sledeći način: pI = e1 : e2 : e3

e1, e2 i e3 su izrazi i to: e1 je izraz čija se vrednost prikazuje, e2 određuje broj pozicija koju zauzima e1 na ekranu, a e3 je broj decimalnih mesta. e2 i e3 su najčešće konstantne vrednosti. Pažnja

Treba voditi računa o tome da kad write završi ispis, ekranski kurzor ostaje odmah iza poslednjeg ispisa, dok ga writeln premešta na početak novog reda. Samim tim, writeln bez parametara samo premešta kurzor na početak sledećeg reda. Za ulaz sa tastature koristimo: read(v1, v2, ..., vN); readln(v1, v2, ..., vN); readln;

37

P R O G R A M S K I

J E Z I K

P A S C A L

Gde je vi ime promenljive čiju vrednost tražimo od korisnika. Ako unosimo više promenljivih odjednom, njihove vrednosti prilikom unosa razdvajamo tako što među njima napravimo jedno ili više praznih mesta.

38

P R O G R A M S K I

J E Z I K

P A S C A L

Potprogrami Prvobitna namena potprograma je bilo skraćivanje izvornog koda, ali je ovo brzo zanemareno. Osnovni razlog upotrebe potprograma je što oni predstavljaju logičku celinu i time sam program postaje pregledniji i lakši za razumevanje. Potprogrami predstavljaju jedan od nivoa modularizacije. Postoje dve vrste potprograma: funkcije i procedure. One imaju istu strukturu koja je gotovo identična opštoj strukturi programa. potprograma predstavlja mehanizam koji se razmenjuju parametri između glavnog programa i potprograma. Oblik zaglavlja zavisi od vrste potprograma. Zaglavlje

function ime(formalni_parametri): tip function ime: tip procedure ime(formalni_parametri) procedure ime

Namena formalnih parametara je prenos ulaznih vrednosti klijenta i prihvatanje eventualnih vrednosti funkcije, odnosno vrednosti koje vraća procedura. Formalni parametri mogu biti promenljive svih osnovnih tipova ili druge procedure. Primeri: function Proizvod(n: integer): longint; var p: longint; i: integer; begin p := 1; for i := 1 to n do p := p * i; Proizvod := p; end;

39

P R O G R A M S K I

J E Z I K

P A S C A L

procedure MinMax(x, y, z: real; var min, max : real); begin if (x < y) and (x < z) then min := x else if (y < x) and (y < z) then min := y else min := z; if (x > y) and (x > z) then max := x else if (y > x) and (y > z) then max := y else max := z; end;

Da bi pozvali neku funkciju, njeno ime ćemo uvrstiti u neki izraz. Proceduru pozivamo samo navođenjem njenog imena u kôdu programa. Svaka funkcija vraća jednu vrednost, tako što njenom imenu dodelimo vrednost u kôdu funkcije. (U primeru funkcije Prozivod, pretposlednja linija kôda dodeljuje vrednost p funkciji.) Pri tome treba voditi računa da se ime funkcije ne sme koristiti u njoj samoj, osim u slučaju dodele konačne vrednosti koju funkcija vraća. Funkcije vraćaju samo skalarne vrednosti. Procedura, s druge strane, ne vraća nikakvu vrednost svojim imenom. Njih koristimo za izmenu stanja programa koje je opisano vrednostima nekih promenljivih, jer će procedura promeniti vrednosti tih promenljivih. Ako je potrebno da je izlazna vrednost potprograma takva da ne može da je vrati funkcija (nije jedna ili nije skalarna) koristićemo proceduru. Deklaracija unapred

Da bi jedan potprogram mogao pozvati drugi, onaj koji je pozvan mora biti deklarisan pre ovog koji poziva. Tj. ako P koristi Q, Q mora biti deklarisano pre P. U slučaju da P poziva Q, ali i Q poziva P nije moguće oba programa smestiti jedan pre drugog, pa koristimo deklaraciju unapred. U zaglavlje jedan potprogram postavljamo prvi i koristimo ključnu reč forward koja prevodiocu označava da će potprogram biti opisan kasnije. Primer: procedure Q(...); forward; procedure P(...); begin ... Q ... end; 40

P R O G R A M S K I

J E Z I K

P A S C A L

Lokalne i globalne promenljive

Pošto se često javlja potreba da u potprogramu postoje neke promenljive koje van njega nemaju značenje, uvodimo lokalne promenljive. Formalni parametri potprograma su lokalne promenljive. Za razliku od ovih postoje i globalne promenljive, koje su definisane u glavnom programu i važe za ceo program. Ako se dogodi da se poklope imena lokalne i globalne promenljive, lokalna promenljiva ima prednost, tj. u potprogramu će važiti lokalna promenljiva, a van njega globalna. Pažnja

Treba voditi računa da lokalna i globalna promenljiva istog imena nikako ne utiču jedna na drugu. Formalni i stvarni parametri

Formalni parametri su oni koji se pojavljuju u zaglavlju potprograma. Pri pozivu tog potprograma njihove vrednosti se zamenjuju vrednostima stvarnih parametara. Stvarni parametri su oni koji se nalaze u potprograma i to mogu biti promenljive ili izrazi.

pozivu

Stvarni i formalni parametri se moraju poklapati po tipu i po redosledu kako se navode u pozivu potprograma, odnosno u njegovom zaglavlju. Parametri vrednosti i parametri imena

Prenos parametara između glavnog programa i potprograma se izvršava preko steka i može biti prenos po vrednosti, kad se prenosi samo vrednost tog parametra, ili po imenu, kad se prenosi adresa parametra. Prenos po vrednosti se koristi za čisto ulazne parametre čiju vrednost u glavnom programu potprogram ne treba da menja. Zbog toga na mestu poziva parametra po vrednosti može da stoji i neki izraz. Prenos po imenu se koristi za ulazno/izlazne parametre. Pošto se u steku nalazi stvarna adresa parametra, 41

P R O G R A M S K I

J E Z I K

P A S C A L

potprogram može da menja vrednost parametra u glavnom programu. Da bi prevodilac znao da li se neki parametar prenosi po vrednosti ili imenu, dodajemo rezervisanu reč var u zaglavlje potprograma. Tako u primeru procedure MinMax x, y i z se prenose po vrednosti, jer ispred njih ne stoji var, a min i max po imenu, jer ispred njih stoji var. Nizovi kao parametri potprograma

Nizove prosleđujemo potprogramu na sledeći način: prvo deklarišemo tip, a zatim u potprogramu navedemo formalni parametar tog tipa. Na primer: type TNiz: array[1..10000] of integer; ... function f(n: integer; Niz: TNiz): integer; begin ... end;

Ovde postoje dva problema: •

niz se prenosi kao parametar vrednosti;

• funkcija će prihvatiti samo nizove koji su identičnog tipa kao formalni parametar. Prvi problem nastaje zbog toga što ako se niz prenosi kao parametar vrednosti, svih n elemenata se prenosi na stek bez obzira koliko nam zaista treba. Ovo zauzima nepotrebne resurse, ali i oduzima vreme pogotovo ako niz ima velik broj članova. Zbog toga možemo dodati var u deklaraciju parametra, da bi se niz preneo po imenu. Tad će potprogram raditi sa stvarnim elementima niza, ali se mora voditi računa ako je niz čisto ulazni da potprogram ne menja vrednost članova. U Turbo Pascalu 7.0 je dozvoljeno da se stvarni parametri prenose na tri načina. Pored prenosa po vrednosti i imenu, uveden je i prenos parametra kao konstante. U tom slučaju se takođe prenosi adresa stvarnog parametra, ali prevodilac vodi računa da potprogram ne menja njegovu vrednost.

42

P R O G R A M S K I

J E Z I K

P A S C A L

U gornjem primeru bismo to rešili na sledeći način: type TNiz: array[1..10000] of integer; ... function f(n: integer; const Niz: TNiz): integer; begin ... end;

Što se tiče drugog problema, on se ogleda u sledećem. Kokretno, u navedenom primeru, funkcija f će prihvatiti kao ulazni parametar neku celobrojnu vrednost n i isključivo celobrojni niz od 10 000 elemenata čiji je opseg od 1 do 10 000. U slučaju da želimo da napišemo neki uopšteni potprogram (npr. za sabiranje n članova niza), za svaki niz koji je drugačiji od ovog bi morali pisati novi potprogram. Ovaj problem je sintaksno rešen tek u Turbo Pascalu 7.0, ali samo za jednodimenzionalne nizove. Kod deklaracije tog niza nećemo navesti njegov opseg. Na primer, procedura P će prihvatiti sve nizove koji su realnog tipa. procedure P(RNiz: array of real); Low() i High()

Ove dve funkcije su uvedene za rad sa otvorenim parametrima. Low nam daje donju granicu niza (najmanji indeks, indeks prvog elementa), a High gornju granicu (tj. najveći indeks, indeks poslednjeg elementa). Da bi mogli koristiti Primer: Low() i High() function Suma(const X: array of prevod iocu integer): real; moram var s: real; i: integer; o begin zadati s := 0; direkti for i := 1 to n do vu

{&P+} .

end;

real; n:

s := s + x[Low(x) + i – 1]; Suma := s

Potprogrami kao parametri drugih potprograma

Potprogramu možemo proslediti neki drugi potprogram kao parametar, tako što ćemo parametru pridružiti odgovarajući tip.

43

P R O G R A M S K I

J E Z I K

P A S C A L

U sledećem primeru smo definisali tip TFunc koji odgovara funkciji g. U deklaraciji funkcije Integral, kao jedan od parametara navodimo f koji je tipa TFunc. type TFunc = function(x: real): real; function Integral(a, b: real; f: TFunc): real; var t: real; begin ... f(t); ... end; function g(x: real): real; begin g := 2 * exp(x) – sin(x) end; ... y := Integral(1.2, g) ... Rekurzivni potprogrami

Rekurzivni potprogram je onaj koji poziva samoga sebe, tj. koristi sopstvene usluge. Prvi put su se rekurzivni potprogrami pojavili u Algolu, jer je tad uveden stek koji je omogućio rekurzivno pozivanje potprograma. Primer rekurzivne funkcije je faktorijel. Rekurzivni programi su najčešće kratki ali nejasni i često utiču na brzinu izvršavanja programa. Treba ih izbegavati, jer uz korišćenje lokalnih promenljivih lako mogu prepuniti stek. Koriste se za rešavanje rekurzivnih problema, npr. prolazak kroz stablo i sl. Primer: function Faktorijel(n: integer): longint; begin if n := 0 then Faktorijel := 1 else Faktorijel := n * Faktorijel(n-1) end;

Moduli



Moduli predstavljaju vrstu autonomnih softverskih jedinica. Da bismo lakše realizovali neki kompleksni program, razbijamo ga na delove od kojih svaki predstavljaja logičku celinu, pa onda te delove zasebno realizujemo i na kraju ih

spajamo. 44

P R O G R A M S K I

J E Z I K

P A S C A L

Moduli su se pojavili 60-ih godina kao rezultat povećanih zahteva korisnika. Prvi moduli su bili vrlo jednostavni i osnovna namena im je bila da se jednom napisan kôd može više puta upotrebiti. U početku

Da ne bismo svaki put kopirali deo kôda koji izvršava isti posao, pojavile su se nisu biblioteke gotovih programa. U početku su bile imale štampane na papiru, pa se proces njihove struk implementacije svodio na prepisivanje turu, kôda sa papira. Kasnije je pronađen predst avljale mehanizam da se ove biblioteke prenose su u prevedenom obliku. skup bibliot eke

neuređ enih Pojavom strukturnih programskih jezika, progra biblioteke modula više nisu predstavljale ma koji se skupove programa opšte namene koji koriste

rešavaju neki problem, već su dobile po potre odgovarajuću organizaciju, tj. strukturu. bi. Važno je napomenuti da Wirthov Pascal Bez modula nema nije poznavao module. Ceo program (sa profesi svim potprogramima) je predstavljao onalne čvrsto vezanu celinu. Prepisivanje kôda je izrade bio jedini način da se izvede da se neki progra potprogram jednog programa izvršava i u ma ni nekom drugom programu. na jednom Za pisanje modula koristimo uobičajene proced uralno komande programskog jezika. Moduli se koriste isključivo u prevedenom obliku i m modul u Pascalu nema progra prevedeni mskom ekstenziju EXE, jer nije izvršni. Najčešće jeziku. ima ekstenziju TPU ili TPP. Šta je modul?

Modul je softverska komponenta koja se realizuje (tj. projektuje, kôdira, testira i modifikuje) autonomno, bez potrebe da se taj kôd meša sa nekim drugim kôdom. Modul može biti izvršni, a ne mora. U Pascalu postoje obe vrste modula. Naime, kod Pascala glavni program je takođe modul (izvršni). Ostale strukture po pravilu nisu izvršni moduli. 45

P R O G R A M S K I

J E Z I K

P A S C A L

Opšta struktura modula Zbog postojanja modula, glavni program pokazuje tendenciju smanjenja. U normalnim okolnostima, sve što se dešava u programu se dešava u modulima. U jedan modul smestićemo srodne funkcije. Ako je već napisan neki modul koji zadovoljava naše potrebe, nikada nećemo pisati neki svoj koji radi isti posao. Opšta struktura modula je Information Hiding principu skrivanja informacija Princip formulisao Parnas početkom le princip nalaže da detalji

saobrazna koji je 70-ih. Ovaj realizacije modula treba da budu skriveni od klijenta. U ovom slučaju klijent je drugi program, ne programer.



Na primer, za realizaciju tačke u Dekartovom koordinatnom sistemu postoje dva načina: pomoću vektora sa dve komponente (jedna predstavlja apscisu, druga ordinatu) i pomoću tipa sloga (gde jedno polje predstavlja apscisu, a drugo ordinatu).. Ova dva načina su potpuno različita, jer svaki način zahteva drugi kôd i sl. ali su ravnopravni – ni jedan nije bolji od drugog. Šta se dešava sa Po principi skrivanja informacija klijent ne ulazo sme znati je li ovaj problem rešen mi kako korišćenjem dvokomponentnog vektora ili se transf sloga. Detalji realizacije su nedostupni ormiše klijentu, čak ako se i promene, klijent to u izlaz, klijent ne sme da primeti. Problem će biti tako a se rešen da modul ima isto ime u oba

ne tiče .

slučaja, iste parametre.

ulazne

i

iste

izlazne

U svojoj strukturi moduli moraju imati dva dela: interfejs modula: klijent ga vidi, to je deo kroz koji se vrši interakcija između klijenta i modula, pa je ovo u stvari deo za spregu sa klijentom. U idealnom slučaju (a skoro uvek je i stvarno tako) interfejs se nikad ne menja, eventualno se dopunjava, jer bilo kakva izmena interfejsa povlači promenu svih poziva klijenata tom modulu; telo modula: ovo je obavezan deo i u Pascalu se zove implementacija. Ovaj deo sadrži pomenute skrivene delove modula.

U modulu se u principu mogu pojaviti sve komponente programskog jezika. U Pascalu se pojavljuju potprogrami, tipovi, imenovane konstante, a malo ređe i promenljive. 46

P R O G R A M S K I

J E Z I K

P A S C A L

Potprogram nije U Turbo Pascalu postoje dve vrste modu modula, svaki sa svojom strukturom: l , jer nije • glavni program (autonomno autono mno realizovan); realizo van, već • modul u užem smislu reči, koji nije kao izvršni kao glavni program i nudi deo modul svoje usluge klijentima. a (tj. glavno Šta se nalazi u modulu g progra Modul pre svega mora predstavljati ma).

logički zaokruženu celinu. Njega odlikuju jaka logička kohezija i slaba logička adhezija. Pod prvim terminom podrazumevamo da modul sadrži logički povezane delove. Drugi termin se odnosi na slabu logičku zavisnost jednog modula od nekog drugog (loose coupling). Oba termina proističu iz zahteva da je modul autonoman.

Uniti u Turbo Pascalu Modul u užem smislu reči u Turbo Pascalu se zove jedinica ili unit. Njena struktura nije identična strukturi glavnog programa zbog svrhe modula i zbog toga što je prilikom pisanja modula najvažnije poštovati princip skrivanja informacija.



Unit se u opštem slučaju sastoji od tri (odnosno četiri) dela. To su: zaglavlje (jednostavno je kao i zaglavlje glavnog programa); interfejs ; implementacija i

inicijalizacija (ovaj deo je opcioni). Zaglavlje unita

Zaglavlje ima jednostavan oblik: sastoji se od službene reči unit i imena jedinice: unit ime_jedinice;

Izbor imena zavisi od verzije Pascala. U Turbo Pascalu ime jedinice mora da se poklopi sa imenom datoteke u kojoj se na disku čuva kôd te jedinice. Interfejs

Označavamo ga službenom reči interface iza koje ne stoji tačka-zarez. Ako naš unit koristi usluge nekog drugog, 47

P R O G R A M S K I

J E Z I K

P A S C A L

onda u interfejs stavljamo referencu na drugi unit. To se radi pomoću: uses ime_jedinice1, ime_jedinice2, ..., ime_jediniceN;

Inače, kad god koristimo procedure i funkcije nekog unita, moramo navesti njegovu referencu, da bi prevodilac znao da na raspolaganju ima još neke potprograme. U interfejsu se takođe mogu naći deklaracije tipova, konstanti, promenljivih i potprograma. Ako je neka procedura ili funkcija iz unita javno dostupna, onda se njeno zaglavlje navodi u zaglavlje unita. Inače, zaglavlja potprograma u interfejsu imaju osobinu deklaracije unapred i zbog toga je redosled deklarisanja potprograma provizoran. Takođe je sve jedno kojim ih redom navodimo u telu jedinice. Implementacija

Označavamo je službenom reči implementation iza koje ne stoji tačka-zarez. U ovom delu se nalazi realizacija procedura i funkcija deklarisanih u interfejsu (njihov kompletan tekst uključujući i zaglavlje). Ukoliko se desi da Pošto su zaglavlja potprograma zajedno deklari šemo sa formalnim parametrima navedena u neki interfejsu, u implementaciji nije obavezno potpro se ponovo deklarišu formalni gram u da interfe parametri. Doduše, ovo se uglavnom radi, jsu, a jer je programeru dok piše potprogram ne realizu lakše da vidi kako je nazvao parametre jemo potprograma i koliko ih ima ako još ga u imple jednom ponovi celo zaglavlje u menta implementaciji. ciji, prevod ilac će Kao što je već rečeno, redosled navođenja nam

i funkcija je irelevantan. prijav procedura iti Takođe svaki potprogram može koristiti grešk neki drugi u okviru unita. Ako su nam u. potrebni i potprogrami drugih unita,

koristimo službenu reč uses. Deo za implementaciju unita podseća na deklaracioni deo glavnog programa i fizički je najveći deo unita. Takođe može da sadrži i lokalne promenljive koje se ne vide van unita. To su interne promenljive koje služe kao 48

P R O G R A M S K I

J E Z I K

P A S C A L

pomoć u implementaciji potprograma i imaju osobine globalnih promenljivih za taj unit. Unit takođe sadrži i lokalne procedure i funkcije (isto nedostupne van njega), a ređe i lokalne tipove i konstante. Kao u interfejsu tako i u implementaciji se mogu naći reference na druge unite i to onda kad se njihove usluge koriste samo interno. Treba voditi računa da kružne reference nisu moguće, jedino u slučaju da se jedan unit referencira u interfejsu drugog, a drugi u implementaciji prvog. Ovo se retko koristi. Inicijalizacija

Inicijalizacija služi za postavljanje nekih početnih stanja u modulu i izvršava se samo jedanput i to kada se u zaglalvju klijenta naiđe na uses . Inicijalizacija se najčešće koristi za postavljanje vrednosti nekih promenljivih i ovo je jedini izvršni deo unita. Inicijalizacija se prepoznaje po tome što

Ovog dela

običn počinje službenom reči begin , iza koje o slede naredbe. nema , jer Bez obzira da li unit ima inicijalizaciju ili ima ne, uvek vrlo specifi tačka. čnu namen u.

se završava sa

end

iza kojeg sledi

Pascalovi gotovi uniti

U Pascalu postoji velik broj gotovih unita. Od njih je najvažniji system koji sadrži sve osnovne funkcije i procedure Pascala. Ovo je jedini unit koji se automatski uključuje u program i ne treba se posebno navoditi. Pored ovog, za rad u tekstualnom režimu u Pascalu bitni su i: • crt (služi za neposredno rukovanje tastaturom i ekranom); • DOS (malo raznovrsniji unit, sadrži potprograme za upravljanje datotekama, direktorijumima itd.); • graph (koristi elemenatima); •

printer

se

za

rad

sa

grafičkim

(komunikacija sa lokalnim štampačem). 49

P R O G R A M S K I

J E Z I K

P A S C A L

Primer



Sledeći program predstavlja unit koji sadrži neke procedure i funkcije potrebne za realizaciju tačke u Dekartovom koordinatnom sistemu. Čuvamo ga u MPOINT.PAS datoteci.

UNIT MPOINT; INTERFACE type TPoint = record x, y: real end; procedure Create(var p: TPoint; xx, yy: real); function GetX(p: TPoint): real; function GetY(p: TPoint): real; procedure Copy(from: TPoint; to: TPoint); function Distance(p1, p2: TPoint): real; IMPLEMENTATION procedure Create(var p: TPoint; xx, yy: real); begin with p do begin x:= xx; y:= yy; end end; function GetX(p: TPoint): real; begin GetX := p.x end; function GetY(p: TPoint): real; begin GetY := p.y end; procedure Copy(from: TPoint; to: TPoint); begin to.x := from.x; to.y := from.y end; function Distance(p1, p2: TPoint): real; begin Distance := sqrt(sqr(p1.x – p2.x) + sqr(p1.y – p2.y)) end; END.

50

2 Tema

Sintaksa programskih jezika Ovde ćemo ukratko izložiti osnovne činjenice o sintaksi prograsmskih jezika i reći nešto o tri najšire korišćena sistem meta-jezika. Aspekti svakog programskog jezika su: •

sintaksa,



semantika i



pragmatika.

Sintaksa sadrži pravila za građenje konstrukcija datog programskog jezika. Čvrsto je vezana za kompajler čiji je osnovni zadatak (pored prevođenja programa na mašinski jezik) da proveri da li je program napisan u skladu sa sintaksnim pravilima. Drugačije rečeno, kompajler proverava da li je neka struktura pravilno napisana i na taj način proverava da li je ta struktura uopšte deo programskog jezika. Sintaksa programskih jezika je po pravilu mnogo jednostavnija od sintakse govornog jezika, ali je zato krajnje stroga. Semantika se bavi značenjem onog što je napisano. To je interpretacija sintaksno pravilno napisane strukture. predstavlja način korišćenja konstrukcija da bi se rešio zadati problem. Pragmatika

jezičkih

Pošto je sintaksa programskih jezika strogo formalna, već krajem 50ih se javila ideja da se formalno i prikaže. 51

Prvi formalni sistem za opis sintakse se pojavio 1960. i opisivao je sintaksu ALGOLa. Za formalni prikaz sintakse se koristi meta-jezik. Metajezik je, dakle, jezik za opis programskog jezika. Isto je formalan i postoji mnogo različitih varijanti meta-jezika. Tri su najpoznatija: BNF, EBNF i sintaksni dijagram. Prva dva se koriste više u svrhu formalnog zadavanja sintakse, a treći je pogodan za njeno prezentovanje. Takođe, prva dva predstavljaju linearnu formu, dok se treći prikazuje dvodimenzionalno. Način za opis sintakse nekog programskog jezika postavlja pred nas jedan osnovni problem: kako predstaviti terminale, a kako neterminale. Terminali su samoobjašnjavajuće konstrukcije nekog programskog jezika. Neterminali su one konstrukcije koje treba dalje razjašnjavati. Na primer, ako posmatramo sledeću naredbu Pascala: while logicki_izraz do naredba;

while i do su terminali, dok su logicki_izraz i naredba neterminali (treba ih definisati). Njih jednim imenom zovemo simboli. Ova tri sistema meta-jezika se bave načinima za zadavanje terminala i neterminala. Pošto kompajleri rade na meta-jezicima, treba obezbediti da formalni sistem predstavi sve moguće oblike neterminala. Kompajler generator

Ulaz za kompajler generator jeste sintaksa novog programskog jezika koja se saopštava odgovarajućim meta-jezikom. Semantiku zadajemo na osnovu nekog već postojećeg programskog jezika (najčešće je to C). Kompajler generator nam onda generiše kompajler tog našeg novog programskog jezika.

Bekus-Naurova forma – BNF John Backus (tvorac Fortrana) i Peter Naur su napravili ovaj, prvi meta-jezik, kojeg definišu sedam stavki. 1. neterminale ;

prikazujemo

52

između

kao:

2. terminali se prikazuju bez ikakvih dodatnih znakova, onako kako se pišu u programskom jeziku; 3. simbol ::= znači jednakost po definiciji, gde se sa leve strane nalazi terminal, a sa desne opis tog terminala u vidu kombinacije neterminala i terminala. Ako se sa desne strane nalazi neterminal, onda njega objašnjavamo na isti način; 4. simbol | označava isključivo ili eksplicitno navedenih elemenata. Na primer: cifra ::= 0|1|2|3| 4|5|6|7|8|9. Ili: slovo ::= A|B|C|D|E|...|Z|a|b|c|d...|z, s tim što se u zadavanju sintakse svi elementi moraju eksplicitno navesti. Ovde to nije slučaj zbog preglednosti; 5. ako lančamo simbole, pišemo ih jedan za drugim, npr: ::= |cifra>; 6. ako konstrukti ne moraju uvek imati istu dužinu koristimo rekurziju, npr: ::= |; 7. postoji i mogućnost ponavljanja nekog simbola jednom ili više puta: {} ili ograničenog ponavljanja {}nm gde je m najmanji mogući broj, a n najveći mogući broj ponavljanja.

Poboljšana Bekus-Naurova forma – EBNF Više se upotrebljava od BNF i posebno je pogodna za kompajler generatore. Postoje određene razlike u odnosu na BNF.



1. neterminali se prikazuju bez ikakvih dodatnih simbola. Ako se neterminal sastoji od više reči, između njih se stavlja crtica ili donja crta, npr: kard-broj ili kard_broj; 2. terminali "while"; 3. simbol

=

se

stavljaju

pod

navodnike,

predstavlja jednakost po definiciji; 53

npr:

4. isključivo ili se predstavlja istim simbolom | ; 5.

lančanje

se vrši na isti način kao kod BNF;

6.

rekurzija

je identična;

7. ponavljanje može biti nula ili više puta (kod BNF je jednom ili više puta). Ograničeno ponavljanje je isto kao za BNF. 8. simbolima srednjih zagrada opcija; 9. simbolima malih zagrada objedinjavanje izraza;

(

i

[

i

]

)

se označava se označava

10. na kraju svakog meta-izraza se stavlja tačka . koja označava njegov kraj. Na primer: "FOR" kont_prom ":=" pv ("TO"|"DOWNTO") kv "DO" naredba ";".

Sintaksni dijagram Kao što je već rečeno, najviše se koristi za prezentaciju sintakse forme i to u glavnom čitaocu u knjigama. Sintaksni dijagram je čitljiviji od BFN i EBNF. Sredstva za prikazivanje sintaksnog dijagrama su grafička. Sintaksni dijagram kao celina ima jednu ulaznu granu na kojoj piše naziv konstrukcije koju definišemo. Terminale prikazujemo krugovima ili zaobljenim pravougaonicima, neterminale pravougaonicima i međusobno ih povezujemo usmerenim granama, pa se lančanje postiže upravo na taj način. Ekskluzivno ili ne postoji. Ponavljanje se postiže isto upotrebom usmerenih grana. Svaka konstrukcija dobijena prolaskom kroz dijagram je pravilna.

54

Primeri

cifra 0

kard_broj

1

2

3

4

cifra

55

5

6

7

8

9

3 Tema

Osnovi teorije algoritama Ovde ćemo videti kako se definiše algoritam i biće dat kraći prikaz tri najpoznatija algoritamska sistema: rekurzivne funkcije, Tjuringove mašine i normalne algoritme Markova.

Šta je to algoritam?

A

lgoritam je vrlo star pojam i ne podleže strogom definisanju (kao i neki pojmovi iz geometrije, npr. tačka, prava i ravan). Dakle, ne postoji stroga definicija algoritma, već se on intuitivno definiše. Reč algoritam potiče od imena arapskog matematičara iz IX veka, a njegovo ime je Abu Jaffar Mohammed ibn Musa al Khowarizmi. Izgovor poslednje dve reči podseća na izgovor reči algoritam. Emiprijska definicija algortima

Algoritam je pravilo formulisano na nekom jeziku koje jednoznačno definiše redosled operacija neophodan za transformaciju dozvoljenih ulaznih podataka u traženi rezultat. Druga definicija je sintetička i formalnija je od ove empirijske. Oslanja se na alfabet (W, skup simbola bez semantike) u okviru kojeg se definiše skup W* – skup reči (nizovi simbola) u kojem se nalazi i prazna reč.

Posmatraćemo dva alfabeta: X i Y, gde je X ulazni alfabet (sadrži dozvoljene ulazne podatke), a Y izlazni alfabet (sadrži izlazne rezultate). X i Y su najčešće isti. Nad skupovima reči X* i Y* se definiše alfabetski operator G, tako da je G ⊆ X* × Y*, pri čemu G nije funkcija. G vrši transformacije reči iz X* u reć iz Y*. Uvešćemo i pojam kodeksa Z koji predstavlja skup zakona koji bliže određuju kako G vrši transformaciju ulazne u izlaznu reč. Odavde sledi sintetička definicija algoritma. Sintetička definicija algortima

Algoritam je uređena četvortka (X*, Y*, G, Z). Iako su X*, Y* i G strogo definisani, Z je definisan intuitivno, pa je i ova definicija intuitivna. Dva algoritma mogu biti ekvivalentni na dva načina: po izvršavanju ili funkcionalno. Ako su isti po izvršavanju, isti ulazni podaci daju iste rezultate. Ako su dva algoritma funkcionalno ekvivalentni, onda im se i kodeksi poklapaju (na primer, ekvivalentni algoritmi imaju isti BDA, ali drugačije nazvane promenljive). Algoritmi su deterministički (za isti ulaz daju isti izlaz) ili sholastički.

Algoritamski sistemi



Algoritamski sistem je sredstvo za zadavanje algoritma. Postoji čitav niz poznatih algoritamskih sistema (npr. opšti BDA koji nije do kraja precizno definisan ili oni vezani za konkretne probeleme, koji opet nisu dovoljno opšti). Tri algoritamska sistema su najpoznatiji i sva tri su ekvivalentna, tj. mogu se svesti jedan na drugi. To su rekurzivne funkcije, Tjuringove mašine i Markovljevi normalni algoritmi.

Rekurzivne funkcije Ovo su najstariji algoritamski sistemi. Vrednost funkcije za neki argument računa se poznatim postupkom na bazi vrednosti funkcije za prethodne vrednosti argumenta. Na primer, ako računamo faktorijel broja n

(n!) imamo za f(0) = 1, a dalje f(i) = i ⋅ f(i-1), i = 1, 2, ..., n. Izračunljive funkc ije su one čija se vredno st može izraču nati algorit mom.

kaže: klasa izračunljivih funkcija poklapa se sa klasom rekurzivnih funkcija i od najvećeg je značaja za aritmetičke funkcije definisane na skupu celih nenegativnih brojeva, tj f : Nk  N. (Aritmetičke funkcije su diskretne.) Churchova teza

Služeći se aritmetičkim funkcijama, možemo napraviti bijektivno preslikavanje skupa reči nekog alfabeta (T*) na skup prirodnih brojeva (N). Znači, sve reči iz skupa T* možemo kôdirati elementima iz N. Osnovna teza teorije algoritama sa stanovišta rekurzivnih funkcija kaže da za svaki algoritam koji obrađuje skupove celih nenegativnih brojeva postoji (funkcionalno) ekvivalentan algoritam koji odgovara nekog rekurzivnoj funkciji. Rekurziju ostvarujemo pomoću šest sredstava, podeljenih u dve grupe: tri bazne funkcije (od kojih moramo poći) i tri osnovne operacije. To su: 1. nula funkcija: preslikava sve argumente u 0, tj. 0n(x1, x2, ..., xn) = 0; 2. funkcija naslednik: vrednost funkcije je jednaka vrednosti funkcije argumenata uvećanog za jedan, tj. S(x) = x', x' = x + 1 ; 3.

projekcijska funkcija:

Inj(x1, x2, ..., xj, ..., xn) = xj ;

4. operacija supstitucije: supstitucija funkcija h1, h2, ..., hm u funkciju g na sledeći način: f(x1, x2, ..., xn) = g(h1(x1, x2, ..., xn), h2(x1, x2, ..., xn), ..., hm(x1, x2, ..., xn)) ; 5. operacija proste rekurzije: kada iz dve funkcije dobijamo treću, npr. rekurzija po y: f(x1, x2, ..., xn, 0) = g(x1, x2, ..., xn, y + 1) i f(x1, x2, ..., xn) = h(x1, x2, ..., xn, y, f(x1, x2, ..., xn, y)). U prostom obliku pišemo: f(0) = a, f(y+1) = h(y, f(y)) ; i 6. operacija minimizacije: izvodi se iz neke aritmetičke funkcije g(x1, x2, ..., xn, y). Ciljna funkcija f(x1, x2, ..., xn) se dobij minimizacijom g,

tako što za vrednost f usvajamo najmanje celo nenegativno rešenje jednačine g(x1, x2, ..., xn, y) = 0. Ako takvo rešenje ne postoji, onda funkcija nije definisana za te vrednosti argumenata. glasi: za funkciju f : Nk  N, k ∈ N ∪ {0} kaže se da je rekurzivna ako postoji bar jedan konačan niz funkcija f1, f2, ..., ft gde je ft ≡ f takav da svaka funkcija fi iz tog niza zadovoljava sledeća dva uslova: Definicija rekurzivne funkcije



fi je bazna funkcija i

• fi se dobija od prethodnih funkcija u nizu supstitucijom, prostom rekurzijom ili minimizacijom. Primer

Sabiranje celih nenegativnih brojeva kao rekurzivna funkcija: f1(x) = I1(x) f2(x) = S(x) f3(x, 0) = I1'(x) f3(x, y + 1) = S(f3(x, y)) = f2(f3(x, y)) f3 ≡ f = x + y.

Tjuringove mašine Pod Tjuringovim mašinama podrazumevamo familiju matematičkih apstrakcija koje predstavljaju jednu apstraktnu mašinu. Ovde posmatramo algoritam sa stanovišta onoga ko ga izvršava, tj. Tjuringove mašine prikazuju apstraktnog izvršioca algoritma i njegovo ponašanje kojim se strogo definisan ulaz transformiše u strogo definisan izlaz. Definisao ih je Alan Turing 1936. godine. Svaki današnji računar predstavlja Tjuringovu mašinu. Kad je 1943. napravljen ENIAC (John von Neumann) on je u suštini predstavljao Tjuringovu mašinu. Zadatak Tjuringove mašine je da reč iz nekog ulaznog alfabeta transformiše u reč nekog izlaznog alfabeta. Zbog toga je glavni problem način zadavanja alfabetskog operatora. UTM se može

svesti na specija lnu.

Postoje dve grupe Tjuringovih mašina i to su specijalne i univerzalne Tjuringove mašine. Današnji računar je po svojoj prirodi jedna univerzalna Tjuringova mašina. Specijalne Tjuringove mašine

Imaju tri funkcionalne celine i to su: beskonačna traka, upisno-čitajuća glava i upravljački blok.

O S N O V I

T E O R I J E

A L G O R I T A M A

• Beskonačna traka igra ulogu memorije i može ih biti jedna ili više, ali su sve ekvivalentne. Na beskonačnoj traci se nalazi ulazna reč i na njoj se izvode sve transformacije. Na početku rada se na beskonačnoj traci nalazi neka ulazna reč od slova, a levo i desno od te reči su prazne ćelije (označavamo ih sa B). • Upisno-čitajuća glava izvršava operaciju učitavanja nekog slova sa ili upisivanja nekog slova na trenutnu poziciju. Ima mogućnost da se pomeri za jedno mesto u levo ili desno U početnom trenutku se nalazi iznad prvog slova ulazne reči.



• Upravljački blok upravlja radom Tjuringove mašine i uvek se nalazi u nekom stanju. Skup stanja upravljačkog bloka nazivamo unutrašnji alfabet Tjuringove mašine i u njemu mora da se nađe tačno jedno početno stanje i jedno ili više završnih stanja. Kad se upravljački blok nađe u jednom od završnih stanja, sesija je završena. Na početku rada je upravljački blok u početnom stanju. Rad Tjuringove mašine se odvija po koracima (pokretima) u tri razdela. Prvo se menja stanje upravljačkog bloka. Zatim se vrši upis slova koje nije B na poziciju iznad koje je upisno-čitajuća glava. Glava se pomera za jednom mesto u levo ili desno. Ovaj postupak se ponavlja dok se upravljački blok ne nađe u nekom završnom stanju. Rezultat rada će biti reč koja se nalazi između dve prazne ćelije. Među simbolima neke reči može se pojaviti i prazan simbol koji ne utiče na

O S N O V I

T E O R I J E

A L G O R I T A M A

reč i označavamo ga sa ε . Treba voditi računa da prazna ćelija (B) ne sadrži nikakav podatak. Samim tim ako sadrži prazno slovo (ε ), ona nije prazna. Ne postoji način da se unapred zna da će za neku ulaznu reč Tjuringova mašina posle konačnog broja koraka doći u jedno od završnih stanja, tj. ne postoji način da unapred znamo hoće li algoritam dati rezultate ili ne. Formalna definicija Tjuringovih mašina

Formalno se Tjuringove mašine definišu kao uređena šestorka (Κ , Γ , Σ , δ , q0, F), gde je: •

Κ – unutrašnji alfabet;

• Γ – spoljašnji alfabet (skup svih simbola koji se mogu naći na beskonačnoj traci uključujući i B i ε ); • Σ – skup ulaznih slova (podskup skupa Γ , u opštem slučaju: Σ ⊆ Γ \ {B}, tj. sigurno B ∉ Σ , ali možda i neki drugi element iz Γ takođe ne pripada Σ ); • δ – funkcija prelaza (obavlja pokrete, a odluka o pokretu se donosi na osnovu stanja upravljačkog bloka i simbola iznad kojeg se trenutno nalazi upisno-čitajuća glava, tj. δ : (Κ × Γ )  Κ × (Γ \ {B}) × {L, R}; •

q0 – početno stanje;



F – skup završnih stanja.

Konfiguracija Tjuringove mašine

Konfiguracija Tjuringove mašine je uređena trojka (q, α , i) i ona određuje njeno ukupno stanje.

O S N O V I

T E O R I J E

A L G O R I T A M A

Ukupno stanje Tjuringove mašine je određeno stanjem upravljačkog bloka (q), aktuelnom reči na beskonačnoj traci (α ) i položajem upisno-čitajuće glave (i), tj. njenim rastojanjem od početka aktuelne reči (redni broj slova iznad kojeg se glava nalazi). Funkcije prelaza

prelaza se zadaje u pesudnonaredbe, npr: qa  q'a'. Funkcija

vidu

Drugi način za njihovo zadavanje (ne isključuje prvi) je u vidu funkcionalne sheme. To je matrica koja po jednoj dimenziji ima oznake stanja, a po kolonama oznake iz spoljašnjeg alfabeta. Na preseku se nalaze urđene trojke (q', a, P). Osnovna teza teorije algoritama sa stanovišta Tjuringovih mašina kaže da se svaki algoritam može zadati u obliku funkcionalne sheme specijalne Tjuringove mašine, tj. svaki algoritam je jedna Tjuringova mašina. Univerzalne Tjuringove mašine

Postavlja se pitanje možemo li definisati Tjuringovu mašinu koja bi mogla predstavljati svaki algoritam. To je univerzalna Tjuringova mašina. Ima tu osobinu da ne služi za izvršavanje jednog algoritma, već svakog algoritma. Funkcionalna shema se kodira, tako da i ona postaje deo ulaza. Univerzalne Tjuringove mašine na ulazu imaju reč koju treba obraditi zajedno sa uputstvom za njenu obradu. U savremenim računarima, uputstva zovemo programima.



Primer



T = (Κ , Γ , Σ , δ , q0, F)



Κ = {S1, S2, S3}

ova

O S N O V I

T E O R I J E

A L G O R I T A M A



Γ = {0, 1, ε , B}



Σ = {0, 1}



q0 = S1



F = {S3}

• funkcionalna shema Tjuringove mašine je: S 1 S 2 S 3

ove

0 1 (S2, 0, R) (S2, 1, R)

ε -

B -

(S2, 1, R) (S2, 0, R)

-

(S3, ε , R) -

-

-

-

O S N O V I

T E O R I J E

A L G O R I T A M A

Normalni algoritmi Markova Ovo je najnoviji algoritamski sistem, izveden je 1954. iz BDA. Posmatraju algoritam sa stanovišta izvršavanja. Osnovni problem kojim se bave NAM je kako prikazati alfabetski operator G na neki standardni način. Kod zadavanja alfabetskog operatora (transformacije ulazne reči) Markov je uočivo da postoje svega dve osnovne operacije: operacija obrade i operacija odluke. Tako da se Markovljevi algoritmi prikazuju primenom ove dve operacije:

i

Postoji i treća operacija, to je tzv. tekućom reči: α  β .

smena

nad

Na početku algoritma se poijavljuje početna vrednost tekuće reči na kojoj se izvršava strogo definisana sekvenca smena na sledeći način: u tekućoj reči se locira prva s leva pojava podreči α i zatim se ona zameni sa β . Na primer: tekuća reč je 9215125415181512, α je 151, a β je 0. Posle smene dobijamo: 920 25415181512.

O S N O V I

T E O R I J E

A L G O R I T A M A

Naravno, ako smena ima više, mora se voditi računa o redosledu njihovog izvršavanja. Tako intuitivno podrazumevamo strogo uređen niz smena koje se primenjuju u tekućoj reči. Ako podreči α dogoditi.

nema, ništa se neće

U svakom slučaju, mora postojati posebna vrsta nula ili više završnih smena koje se izvršavaju ako ni jedna druga više ne može da se izvrši. Njih označavamo tako što stavimo tačku iza strelice, npr. α  . β . Uobičajena oznaka za prazno slovo je Λ . Proširivanje reči radimo na sledeći način: Λ  β , tj. ispred tekuće dodajemo β . Definicija normalnih algoritama Markova

MNA definišemo definicije: •

σ



x je reč;

i

koristeći

sledeće

je smena;

• P(σ i, x) je predikat, tj. x = σ i(x) • K(σ i) pokazuje T ako je σ završna smena, tj. ⊥ ako nije.

i

O S N O V I

T E O R I J E

A L G O R I T A M A

Opšti oblik Markovljevih normalnih algoritama

Opšti oblik Markovljevih algoritama prikazan je šemom:

normalnih sledećom

početak ulaz: X

P(σ 1, x)

DA

x = σ 1(x)

NE

P(σ 2, x)

DA

x = σ 2(x)

DA

K(σ )

2

K(σ i) DA

1

kraj

x = σ i(x)

NE

)

DA

NE

izlaz: X

1

DA

NE

P(σ i, x)

K(σ

1 NE

1

NE

1

O S N O V I

T E O R I J E

A L G O R I T A M A

Fundamentalne osobine algoritama To su diskretnost, primenljivost, determinisanost i masovnost. • Diskretnost je definiciona osobina, jer kontinualni algoritmi ne postoje • Algoritam je primenljiv na date ulazne podatke ako nakon konačnog broja koraka daje rezultate. • Detereminisanost znači da algoritam za iste ulazne podatke daje isti rezultat. To važi samo za determinističke algoritme. Pored njih postoje i sholastički koji nemaju ovu osobinu, pa ovo nije esencijalna osobina algoritama. • Masovnost znači da je algoritam primenljiv na veliki broj ulaznih podataka. Tu postoje određeni problemi. Prvo, ne znamo šta znači veliki broj ulaznih podataka Dalje, postoje algoritmi koji nemaju ulazne podatke. Treće, neki algoritmi se pišu za samo jedan tačno određeni ulazni podatak.

4 Tema

Strukturirano programiranje subtitle

S

trukturirano programiranje predstavlja tehnologiju za sistematsku izradu softvera. Definisano je kao paradigma 70-ih godina koja je kasnije dopunjena sistem analizom. Tada je konačno postalo moguće pratiti ceo životni ciklus softvera. Metodologija koja je prethodila strukturiranom programiranju je kompozitno programiranje. Korišćeno je od pojave viših programskih jezika, pa do kraja 60ih, kad je nastala prva velika softverska kriza. Prva velika softverska kriza je bila uzrokovana činjenicom da nije postojao softver koji bi mogao iskoristiti novonastale velike mogućnosti hardvera. Zbog toga su organizovani stručnjaci koji su dijagnostifikovali da je problem u tome što se u to vreme kompleksan softver nije mogao pisati bez grešaka. 1968. holandski hemičar E. W. Dijkstra piše čuveni kontroverzni članak: „Goto Statement Considered Harmful“ u kojem izlaže rezultate istraživanja po kojima je broj grešaka u softveru proporcionalan broju upotreba goto naredbe. Konačno je 1969. definisan programski jezik koji je omogućio pisanje programa bez upotrebe goto naredbe. U pitanju je Pascal Niklausa Wirtha. 1972. Dijkstra, Hoare i Dahl su napisali knjigu „Structured Programming“ po kojoj je cela ova oblast i

dobila ime. U toj knjizi se nalaze tri članka koji se između ostalog bave rešavanjem problema bez upotrebe goto i strukturama podataka. Tih godina je razrađena i teorija modula. Najveću zaslugu za to ima Parnas koji je definisano čuveni Princip skrivanja informacija (IHP). Ovde

je

bitno

pomenuti i sukcesivnu (hijerarhijsku) Znači, od najkompleksnijeg načina zadavanja problema njegovim rastavljanjem na jednostavnije celine dolazimo do određene proizvoljne dubine. dekompoziciju programa.

Pod strukturiranim programiranjem podrazumevamo skup tehnika za razvijanje programskih modela koji koriste strogo definisane upravljačke strukture i strukture podataka. Sredstva za analizu algoritama

Osnovno sredstvo za analizu algoritma jeste graf toka programa. To je digraf (orjentisani graf) koji pokazuje redosled izvršavanja operacija u programu. Graf toka programa možemo definisati i kao BDA redukovan na najosnovnije elemente. Graf toka programa se sastoji od tri elementa: 1.

funkcionalni čvor (proces)

transformiše neke

informacije. Ima jedan ulaz i jedan izlaz; 2.

predikat

3.

kolektor

ima jedan ulaz i dva izlaza; ima dva ulaza i jedan izlaz.

Primer

T ⊥

T ⊥

Na

gornjem dijagramu možemo uočiti jedan od Njega možemo prikazati posebnim simbolom obrade ako želimo da uprostimo glavni graf. podgrafova.

Pravilni programi Klasa programa koja se razmatra kao ciljna klasa se zove pravilni program. Pravilni program zadovoljava tri uslova: 1. ima tačno jednu ulaznu granu 2. ima jednu izlaznu granu 3. kroz svaki čvor prolazi se bar jednom od ulaza do izlaza Prva dva uslova nalažu da ceo pravilni program možemo zameniti tačno jednim čvorom, a treći uslov eliminiše mogućnost beskonačnih ciklusa i izolovanih (nedostupnih) delova programa. Pravilni potprogram

Pravilni potprogram je sastavni deo nekog programa koji je takođe pravilan. Na datom primeru grafa toka programa zaokružen je jedan podgraf koji je takođe i pravilan program.

Potprograme uvodimo zbog načina uočavanja dekompozicije programa na prostije delove. Dekompoziciju vršimo dokle je god moguće poštovati pravilnost potprograma. Dakle, pronalazimo potprograme koji ne mogu da se dekomponuju na pravilne potprograme. Takvi potprogrami su prosti potprogrami.

Prost program je pravilan program čiji svaki pravilan potprogram ima tačno jedan čvor. Dakle, prost program može da sadrži druge pravilne programe, ali to su samo procesi i ništa više. Odavde vidimo da prosti programi predstavljaju neke od osnovnih naredbi jednog programskog jezika.

Analiza algoritama Analiza algoritama se direktno bavi problemom koji iskazi treba da postoje u nekom programskom jeziku. Izgrađena je oko dva centralna pojma: pojma pravilnog programa i pojma prostog programa. Kao i dva algoritma isto i dva programa mogu biti funkcionalno ekvivalentni (za isti ulaz generišu isti izlaz) i ekvivalentni po izvršavanju (ako ulazne podatke obrađuju na isti način do izlaznih). Analiza i odnos prostih programa nas navode na određivanje pojma baze strukturiranih programa. To je skup prostih programa čijom se superpozicijom (kombinovanjem) može realizovati svaki pravilan program. Treba skrenuti pažnju da baza strukturnih programa po definiciji nije minimalna, tj. ona može da sadrži i „višak“ prostih programa. Dakle, baza je redundantna. Strukturirani program je program sastavljen od prostih programa iz zadate baze. Dakle sledi da je strukturiran program pravilan program, ali to ne znači da u njemu po definiciji nema skokova. Pojam strukturiranog progama je relativan, jer se definiše u odnosu na zadatu bazu. Tako je jedan program u odnosu na jednu bazu strukturiran, ali ne mora biti strukturiran u odnosu i na neku drugu bazu.

Kao kandidati za sastavljanje baze razmatraju se programi sa jednim, dva, tri i četiri čvora, ali naravno ne svi koji zadovoljavaju ovaj kriterijum (npr. oni koji nemaju funkcionalne čvorove nisu od interesa za bazu, jer „ne rade ništa“). Ovakvih programa ima sedam: 1.

f

2 .

f

3 .

T P

4 .

funkcionalni čvor

g

sekvenca

S ⊥

IF-THEN

S T

P

WHILE-DO



5 . 6 .

⊥ S

P

REPEAT-UNTIL

f

T P

IF-THEN-ELSE



7 .

T

g g f

T P



DO-WHILE-DO

Programi 3 i 6 su programi selekcije, a 4, 5 i 7 iteracije. Ovi prosti programi predstavljaju osnovne naredbe svih strukturnih programskih jezika, tj. preslikavaju se u naredbe tih programskih jezika. Odavde vidimo da je skup naredbi u Pascalu teorijski zasnovan.

Primetimo da među ovih sedam prostih programa ne postoji ekvivalent pascalske naredbe case. To je zbog toga što case nije prost program, jer ga možemo zameniti pomoću if-then-else strukture. Razlog za uvođenje case naredbe je taj što nam je tako programiranje olakšano. Takođe primetimo da u realizaciji ovih sedam struktura ne postoje skokovi.

Strukturna teorema i programiranje bez goto Izdvaja se jedna posebna baza strukturnih programa. Nju čine sekvenca, iteracija tipa while-do i selekcija tipa if-then-else. Pomoću ove baze se može izvesti svaki program i na osnovu nje pravimo druge baze koje su nam potrebne. 1966. C. Boehm i G. Jacopini su izveli strukturnu teoremu koja pokazuje da se svaki pravilan program može ostvariti superpozicijom ove tri strukture, tj. da se može ostvariti bez upotrebe skokova, što je bilo ključno za rešavanje problema koji su tražili uvođenje strukturiranih programa. Za dokaz teoreme uzećemo jedan pravilan program prikazan sledećim grafom toka programa: Naš cilj je da definišemo program funkcionalno ekvivalentan ovom koji se može sastaviti upotrebom samo pomenute tri strukture.

0

A

1

2 T

3 P



B

Uočićemo redosled izvršavanja čvorova sa GTP i pokazaćemo da se može obezbediti mehanizam za određivanje sledećeg čvora koji će biti izvršen, a da taj mehanizam sadrži samo tri date

strukture. Da bismo to postigli, prvo ćemo na GTP proizvoljnim simbolima obeležiti čvorove (na primeru, uzeti su celi nenegativni brojevi). Okolinu (ono što se na grafu nalazi pre ulaza i pre izlaza) isto obeležavamo

proizvoljnim simbolom (uzeta je 0). Kolektore ne označavamo, jer su to pomoćni simboli GTP. Označavanjem čvorova se u stvari omogućava da svaka ta oznaka bude argument pomenutog mehanizma čiji je rezultat takođe oznaka čvora. Zatim, definišemo matricu prelaza koja čini osnovu za određivanje koji će se čvor sledeći izvršiti. To je kvadratna matrica koja ima onoliko vrsta i kolona koliko ima oznaka čvorova uključujući i oznaku okoline. Realizujemo je kao logičku matricu, tako što je popunjavamo po vrstama. Za svaki čvor u nekoj vrsti upisujemo T i ⊥ . T upisujemo u onoj koloni koja odgovara čvoru koji neposredno sledi čvoru iz date vrste. Zbog toga u svakoj vrsti postoji samo jedno T. Pošto dva čvora mogu da slede neki predikat (ili jedan ili drugi, ne oba) u odgovarajuće kolone nećemo staviti oznake T i ⊥ , već stavljamo P i P , od kojih opet samo jedna može biti tačna i ona nam pokazuje koji se čvor sledeći izvršava. Matrica prelaza za gornji graf toka programa izgleda ovako: 0 1 2 3

0 ⊥ ⊥ ⊥ T

1 T ⊥ P ⊥

2 ⊥ T ⊥ ⊥

3 ⊥ ⊥ P ⊥

Sada, definišemo funkciju next koja preslikava skup oznaka čvorova na samog sebe: function next(n: integer): integer; begin j := 0; while not L[n, j] do j := j + 1; next := j; end;

Sad možemo napraviti prelaznu strukturu koja vodi krajnjoj strukturi. Koristićemo još jednu teoremu, to je teorema o kanoničkoj formi. Ona kaže: za svaki pravilan program postoji ekvivalentan program koji se sastoji od jedne iteracije while-do unutar koje se nalazi struktura case sa onoliko paralelnih grana koliko ima procesa i predikata u grafu toka programa.

Dakle, kanonička forma za naš primer je: begin n := 1; {oznaka prvog čvora koji se izvršava} while n 0 do {0 je oznaka okoline} case n of 1: begin {obraditi n}; n := next(n) end; 2: begin {obraditi n}; n := next(n) end; ... k: begin {obraditi n}; n := next(n) end; end end;

Sada možemo formulisati strukturnu teoremu: svaki pravilan program može se transformisati u ekvivalentan formalno strukturiran program uz korišćenje tri osnovne upravljačke strukture: sekvence, iteracije tipa while-do i selekcije tipa if-then-else. Primetimo da se case ne uklapa u ovu teoremu, ali tu strukturu vrlo lako zamenjujemo ugnežđenom strukturom if-then-else. Minimalna baza strukturiranih programa

Baza koja se sastoji od sekvence, iteracije while-do i selekcije if-then-else nije minimalna. Možemo isključiti strukturu if-then-else (tj. zameniti je strukturom whiledo) i onda opet prikazati pravilne programe pomoću struktura iz ove (sada minimalne) baze. If-then-else možemo zameniti sa while-do na sledeći način:

if p then A else

 q := p ;r := n o pt ;  B ⇔  w h i qld eob e g Ai ,qn := n o qt e n d ;  w h i rld eob e g Bi ,rn:= n o rt e n d ; 

Uzroci nestrukturiranosti programa Strukturiran program je relativan pojam, jer se odnosi na bazu. Ukoliko ne postoji referenca na neku bazu, pojam strukturiranog programa se odnosi na programe koji su pisani bez upotrebe skokova ili uopšte koji se odnose na bazu izgrađenu od sekvence, iteracije tipa while-do i selekcije tipa if-then-else. Postoji pet uzroka za nestrukturiranost programa, tj. postoji pet struktura koje ako se neka od njih pojavi kao podgraf GTP onda je program nestrukturiran i mora se koristiti goto. To su sledeće strukture:

S T R U K T U R I R A N O

P R O G R A M I R A N J E



nepravilan put iza selekcije

T



T ⊥



repeticija sa više izlaza

T ⊥

⊥ T

S T R U K T U R I R A N O

P R O G R A M I R A N J E



repeticija sa više ulaza

T ⊥

T ⊥



repeticije koje se preklapaju

T ⊥

T ⊥

S T R U K T U R I R A N O

P R O G R A M I R A N J E



paralelne repeticije (ovo je inače i nepravilan program)

T

T ⊥



S T R U K T U R I R A N O

P R O G R A M I R A N J E

Strukturiranje programa Strukturiranje programa se bavi problemom eliminisanja skokova. Oni ostaju kao opcija jedino za eventualno iskakanje iz nekog ciklusa. Skokove eliminišemo transformacijom programa koji ih koristi u funkcionalno ekvivalentni koji ih nema. Postoje dva načina za ovu transformaciju: možemo napisati program iz početka izbegavajući skokove ili koristiti neku od metoda za transformaciju. Ovaj drugi metod je više korišćen. Najpoznatija metoda je Aschroft-Manna i bazira se na dokazu strukturnih teorema.

Metoda Aschroft-Manna Primer

Transformisaćemo program dat sledećim GTP u funkcionalno ekvivalentan program. To se radi na sledeći način:

0

1

A

2

T

P ⊥

3

B 4

Q ⊥

T

S T R U K T U R I R A N O

P R O G R A M I R A N J E

• prvo okolinu;

označimo

čvorove

i

• uvodimo jednu upravljačku promenljivu, sukcesivnim proveravanjem njene vrednosti izvršiće se odgovarajući čvor; • zbog ubrzanja procesa, vrednost idućeg čvora nećemo tražiti u matrici prelaza, već se ta vrednost zadaje eksplicitno. Ova metoda je izuzetno pogodna za automatizaciju. Bazira se na transformaciji nestrukturiranog programa (sa goto skokovima) u program bez goto naredbi.

Evo kako programa:

izgleda

algoritam

tog

S T R U K T U R I R A N O

P R O G R A M I R A N J E

n1

n=1

T



n=2

A n2

T





n1

n3

T



n=4

T





n=3

P

B n4

T

Q

T

n2

⊥ n0

n=0 T

Pisanje programa bez upotrebe skokova Ovo ostvarujemo hijerarhijskom (ili sukcesivnom) dekompozicijom programa. Najčešće se koristi topdown razvoj programa koji predstavlja vrlo prirodan način za pisanje

S T R U K T U R I R A N O

P R O G R A M I R A N J E

programa uopšte rešavanje kompleksnih problema. Program razbijamo na jednostavnija rešenja koja se sukcesivno izvršavaju. Primer

• Rešiti sistem linearnih jednačina Ax = b reda n, Ax = b, n. Sa teorijskog aspekta, jasno je da problem rastavljamo na problem unosa podataka, određivanja rešenja i izdavanja rezultata. Naravno, svaki od ovih problema se može dekomponovati na još jednostavnije probleme. Tako da dekompoziciju problema vršimo do nivoa naredbe programskog jezika. Svaki od ovih nivoa dok ne stignemo do naredbe u stvari predstavlja komentar u programu koji opisuje postupak rešavanja problema. REŠITI Ax = b, n

ulaz: n, A, b

odrediti rešenja

odrediti A-1

izlaz x

odrediti x = A-1 b

Rešavanje sistema linearnih jednačina Ax = b, n.

U praksi, ulaz i izlaz nećemo dekomponovati na jednostavnije probleme, jer su jasni sami po sebi. Doduše, problem određivanja rešenja dekomponujemo na neke podkorake čiji broj zavisi od toga kako rešavamo sistem. Na slici je dat jedan primer. Ispod ovog nivoa nećemo ići kad je u pitanju ovaj zadatak, jer su dalji postupci jednostavni.

S T R U K T U R I R A N O

P R O G R A M I R A N J E

Ovakav način rešavanja problema otvara mogućnost paralelnog timskog rada, zbog toga što su poslovi algoritamski nezavisni. Druga mogućnost koja nam se otvara jeste korišćenje gotovog softvera. Ova metoda više nije toliko popularna, njen osnovni nedostatak je taj što je algoritamski orijentisana.

5 Tema

Strukture podataka U dosadašnjem izlaganju najviše reči je bilo o algoritmima. Ovde ćemo se baviti jednom podjednako bitnom komponentom svakog programa – strukturama podataka.

Pojam podatka

N

ajosnovniji pojam u računarstvu uopšte jeste pojam podatka. U memoriji računara se ne nalazi ništa osim podataka (Van Neumannova koncepcija). Tu se misli na podatke u užem smislu reči (vrednosti nekih promenljivih) i uputstva kako se ti podaci obrađuju (programi). Pre konstruisanja ENIAC-a, program je bio realizovan mehanički. Podaci odgovaraju diskretnim, zapisanim činjenicama o fenomenima iz kojih se izvlače informacije. Informacija je svako povećanje znanja. Jedinice posmatranja zovemo entiteti. Entitet predstavlja stvar, subjekt, pojam ili događaj. Svaki podatak se vezuje za neki entitet, pa podatak definišemo kao uređenu četvorku: (naziv entiteta, naziv osobine, vrednost osobine, vreme). Na primer: (Petar, težina u kg, 70, decembar 2002.). Da bi mogli manipulisati podacima, oni moraju biti uređeni. Podatke možemo definisati kao skupove. Na primer, treba proveriti da li je data ulazna vrednost (10) elemenat skupa S = {-5, 10, 11, 12, 25}. Znači,

treba kreirati odgovarajući algoritam koji će porediti datu vrednost sa elementima skupa, tj. sa njegovim prvim, drugim itd. elementom. Postavlja se pitanje kako odrediti redosled elemenata, ako znamo da je skup neuređena struktura. Dakle, mora postojati bar jedno uređenje podataka. Tako da, ako skup S snabdemo uređenjem, to više neće biti skup. Uređen skup se zove struktura. Bez strukture podataka nema programa. Prvi programski jezici (Fortran, BASIC, Cobol) su bili prilično siromašni strukturama podataka. Strukture podataka i tipovi podataka su vrlo bliski pojmovi. U Pascalu, Wirth je izjednačio po značaju algoritamski i deklarativni deo (u kojem su date strukture podataka). On je pokazao da je struktura podataka potpuno ravnopravna u odnosu na algoritam i da je to stabilniji deo programa.

A1

A2

Ai

An

S Odnos algoritma i strukture podataka

Ako posmatramo gornju šemu gde su prikazani algoritmi A1, A2, ..., An koji svi pristupaju nekoj strukturi podataka S, jasno je da izmena nekog od algoritama Ai verovatno neće imati uticaj na drugih n-1 algoritama. Ali svaka izmena strukture S će gotovo sigurno izazvati izmene u skoro svim algoritmima.

Značaj struktura podataka Značaj struktura podataka ilustrovaćemo na primeru. Tu se jasno vidi koliki je uticaj struktura podataka na algoritam.



Treba napisati Pascal program za manipulaciju sa matricama. Program treba da omogući pristup elementima matrice, obradu tih elemenata, brisanje, dodavanje novih itd. Postavlja se pitanje koju strukturu podataka će koristiti naš program. Uzećemo da je matrica definisana kao izvedeni pascalski tip array: var A: array[1..m, 1..n] of ;

U ovom slučaju, pristup elementima rešen je na nivou programskog jezika: A[i, j] := t; m := A[k, l];

A takođe i obrada elemenata: for i := 1 to m do for j := 1 to n do Obradi(A[i,j]);

Postavlja se pitanje, kako manipulisati matricom ako su m i n veliki brojevi. Npr, ako su m i n 1000, naša matrica ima milion elemenata, što traži prilično velik memorijski prostor za smeštanje tih elemenata. Takođe, pristup i obrada elemenata može trajati prilično dugo. Dakle, učinićemo znatno ozbiljniji zahvat i razmotrićemo druge načine za realizaciju strukture podataka. U inženjeriji je vrlo čest slučaj da u matricama sa velikim brojem elemenata, vrednost najvećeg broja tih elemenata je nula. Takve matrice se zovu retke matrice. Zašto onda ne bismo obratili pažnju samo na one elemente čija je vrednost različita od nule? Prvo, memorisaćemo samo one čija je vrednost različita od nule. Time smo matricu pretvorili u niz slogova (p je mnogo manje od m i n.): var B: array[1..p] of record vrednost: T; vrsta, kolona: integer end;

Sada obrađujemo samo elemente od B: for k := 1 to p do obradi(B[k].vrednost);

Ovim smo smanjili troškove memorije i vreme obrade, ali sada pristup elementima A više nije jednostavan. Pristup elementu A[i,j] možemo rešiti na primer na sledeći način: k := 1; nadjen := false; while (k
View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF