31
1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2, SI1P2 5 6 Rešeni ispitni zadaci 7 8 Radna verzija skripte sa rešenim ispitnim zadacima na kursevima OO1P2 i SI1P2 Ovaj materijal je podložan promenama i može sadržati greške Za sve uočene nedostatke i nejasnoće, obratiti se predmetnom asistentu: [email protected] [email protected] Verzija: 0.4 13. juni 2014 9

Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

  • Upload
    others

  • View
    12

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

1

Univerzitet u Beogradu 2

Elektrotehnički fakultet 3

Programiranje 2 4

OO1P2, SI1P2 5

6

Rešeni ispitni zadaci 7

8

Radna verzija skripte sa rešenim ispitnim zadacima na kursevimaOO1P2 i SI1P2

Ovaj materijal je podložan promenama i može sadržati greškeZa sve uočene nedostatke i nejasnoće, obratiti se predmetnom asistentu:

[email protected]@etf.bg.ac.rs

Verzija: 0.413. juni 2014

9

Page 2: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Sadržaj 10

1 Ispitni zadaci 2 11

1.1 Jun 2011 - zadatak 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 12

1.1.1 Analiza problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 13

1.1.2 Analiza rešenja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 14

1.1.3 Drugo rešenje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 15

1.2 Jun 2012 - zadatak 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 16

1.2.1 Analiza Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 17

1.2.2 Strukture podataka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 18

1.2.3 Definisanje koraka u rešenju . . . . . . . . . . . . . . . . . . . . . . . . . . 12 19

1.2.4 Glavni program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 20

1.2.5 Implementiranje pozivanih funkcija . . . . . . . . . . . . . . . . . . . . . . 13 21

1.2.6 Kompletan program i zaključak . . . . . . . . . . . . . . . . . . . . . . . . 20 22

1.2.7 Drugo rešenje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 23

1

Page 3: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Glava 1 24

Ispitni zadaci 25

Zadatak 1.1 Jun 2011 - zadatak 1 26

Neka se u datoteci teniseri.txt nalaze podaci o teniserima po sledećem formatu: šifra 27

igrača (ceo broj), ime (najviše 30 znakova), prezime (najviše 30 znakova) i broj poena na ATP 28

rang listi (ceo broj). U datoteci wimbledon.txt se nalaze podaci o plasmanu tenisera na 29

ovom teniskom turniru. U svakom redu datoteke se nalazi šifra tenisera (ceo broj), broj poena 30

koji brani na turniru (ceo broj) i broj poena koji je osvojio na turniru (ceo broj). Raspored 31

tenisera u datotekama ne mora biti identičan, niti svi teniseri iz prve datoteke moraju postojati 32

u drugoj datoteci. Napisati program na programskom jeziku C koji pročita sadržaj ulaznih tekst 33

datoteka i formira jednostruko ulančanu listu, a zatim u izlaznu datoteku atplista.txt za 34

svakog tenisera upiše novi broj poena po formatu kao u prvoj ulaznoj datoteci. Novi broj poena 35

se dobija tako što se od starog broja poena na ATP listi oduzme broj poena koje teniser brani, a 36

zatim doda broj poena koje je teniser osvojio na turniru. Voditi računa o ispravnom korišćenju 37

zauzetih resursa. 38

1.1.1 Analiza problema 39

Za razliku od prethodnog zadatka, jedan od postavljenih zahteva je obrada dve ulazne 40

datoteke i formiranje treće datoteke. Ključna stvar koju bi trebalo ustanoviti jeste da li je 41

neophodno učitavati podatke iz obe datoteke u memoriju i čuvati ih u vidu ulančane liste ili ne. 42

Ako je problem moguće rešiti tako da se sadržaj jedne od datoteka ne mora čitati više puta, 43

tada se njen sadržaj ne mora čuvati tokom izvršavanja programa (u vidu liste npr.). 44

Sledi spisak zahteva koje bi trebalo ispuniti: 45

1. Otvaranje datoteka; 46

2. Formiranje jednostruko ulančane liste; 47

3. Računanje novih poena za svakog od igrača čiji su podaci sadržani u prvoj datoteci; 48

4. Formirnaje izlazne datoteke; 49

5. Oslobađanje zauzetih resursa (datoteke i dinamička memorija). 50

Prva datoteka predstavlja spisak tenisera, sa njihovim trenutnim bodovima na ATP listi, 51

dok se druga datoteka koristi za ažuriranje broja bodova. Svaki od tenisera iz prve datoteke 52

može se naći najviše jedanput u datoteci wimbledon.txt. Prema tome, da bi se sračunali novi 53

poeni, potrebno je za tenisere koji su učestvovali na turniru Wimbledon pronaći odgovarajuće 54

2

Page 4: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

podatke iz prve datoteke, i potom promeniti broj poena. Kako je za svaki red druge datoteke 55

neophodno pronaći trenutni broj poena tenisera (iz prve datoteke), zaključuje se da će se više 56

od jednog puta pretraživati podaci iz prve datoteke. Sa druge strane, jedan red druge datoteke 57

biće korišćen samo jedanput – radi modifikovanja poena tenisera koji je njime predstavljen. 58

Uzevši to u obzir, donosi se odluka da se sadržaj prve datoteke predstavi preko ulančane 59

liste (zbog višestruke pretrage), a da se sadržaj druge datoteke jednostavno učitava red po red, 60

iskoristi, i potom pređe na naredni. 61

Listing 1.1 sadrži kôd rešenja ovog zadatka. Budući da je obrada jednostavnija, moguće je 62

sve obaviti unutar glavne funkcije. 63

3

Page 5: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.1: Kompletan program64

1 #include <stdio.h> 65

2 #include <stdlib.h> 66

3 #include <string.h> 67

4 #define MAX_NAME_ARRAY_LENGTH 31 68

5 69

6 typedef struct elem { 70

7 int playerId; 71

8 char firstName[MAX_NAME_ARRAY_LENGTH]; 72

9 char lastName[MAX_NAME_ARRAY_LENGTH]; 73

10 int points; 74

11 struct elem *next; 75

12 } Elem; 76

13 77

14 int main () { 78

15 Elem *head = NULL, *tail = NULL, // first and last elements of the list 79

16 *newElem, *current; // auxiliary variables 80

17 FILE *input1, *input2, *izlaz; 81

18 82

19 int playerId, pointsToDefend, pointsWon, points; 83

20 char firstName[MAX_NAME_ARRAY_LENGTH], lastName[MAX_NAME_ARRAY_LENGTH]; 84

21 85

22 if ((input1 = fopen("teniseri.txt","r")) == NULL) { 86

23 printf("Greska u otvaranju ulazne datocurrente teniseri.txt!"); 87

24 exit(1); 88

25 } 89

26 if ((input2 = fopen("wimbledon.txt","r")) == NULL) { 90

27 printf("Greska u otvaranju ulazne datocurrente wimbledon.txt!"); 91

28 exit(1); 92

29 } 93

30 if ((izlaz = fopen("atplista.txt","w")) == NULL) { 94

31 printf("Greska u otvaranju izlazne datocurrente atplista.txt!"); 95

32 exit(4); 96

33 } 97

34 98

35 while(fscanf(input1, "%d%s%s%d", &playerId, firstName, lastName, &points)!=EOF) { 99

36 if ((newElem = malloc(sizeof(Elem))) == NULL) { 100

37 printf("Greska u alociranju memorije!"); 101

38 exit(3); 102

39 } 103

40 104

41 newElem->next = NULL; newElem->points = points; newElem->playerId = playerId; 105

42 106

43 /* Copying string content from buffers firstName and lastName into 107

corresponding fields of the element structure. */ 108

44 strcpy(newElem->firstName, firstName); 109

45 strcpy(newElem->lastName, lastName); 110

46 111

47 if (head == NULL) head = newElem; 112

48 else tail->next = newElem; 113

49 tail = newElem; 114

50 } 115

51 116

52 while(fscanf(input2, "%d%d%d", &playerId, &pointsToDefend, &pointsWon)!=EOF) { 117

53 current = head; 118

54 while (current != NULL && current->playerId != playerId) 119

55 current = current->next; 120

56 if (current != NULL) 121

57 current->points += (pointsWon - pointsToDefend); 122

58 } 123

4

Page 6: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

59 124

60 current = head; 125

61 while (current != NULL) { 126

62 fprintf(izlaz,"%d %s %s %d\n", current->playerId, current->firstName, 127

63 current->lastName, current->points); 128

64 current = current->next; 129

65 } 130

66 131

67 current = head; 132

68 while (current != NULL) { 133

69 tail = current; 134

70 current = current->next; 135

71 free(tail); 136

72 } 137

73 fclose(input1); fclose(input2); fclose(izlaz); 138

74 return 0; 139

75 } 140141

1.1.2 Analiza rešenja 142

Nakon što se otvore ulazne i izlazna datoteka, i proveri uspešnost otvaranja (što je neophodno 143

uraditi), prelazi se na učitavanje sadržaja prve datoteke input1. Učitavanje se obavlja u while 144

petlji, i pritom se koristi funkcija fscanf, kako bi se učitavanje obavilo na najjednostavniji 145

način. Na ovom mestu napominjemo da se pri učitavanju istovremeno i proverava da li se 146

čitanjem došlo do kraja datoteke; ako jeste, čitanje se ne nastavlja. Takođe napominjemo da bi 147

bilo nekorektno proveravati da li postoji još sadržaja u datoteci primenom funkcije feof()1, 2. 148

Dakle, kôd kakva je dat u listingu 1.2 nije korektan i ne preporučuje se upotreba funkcije feof 149

na taj način. 150

Listing 1.2: Primer nekorektne upotrebe funkcije feof()

1 while(!feof(ulaz1)){2 fscanf("%d%s%s%d", &sifra, ime, prezime, &bodovi);3 //...4 }

Podaci se pritom učitavaju u lokalne podatke (1.3), a potom kopiraju u odgovarajuća polja 151

dinamički alociranog elementa newElem. Učitane tekstualne podatke je neophodno kopirati 152

pomoću funkcije strcpy u odgovarajuće stringove alocirane strukture (v. listing 1.4). Operator 153

dodele nije moguće ovde upotrebiti, jer se radi o statičkim nizovima karaktera unutar strukture 154

Elem. 155

Listing 1.3: Lokalne prihvatne promenljive

1 int playerId, pointsToDefend, pointsWon, points;2 char firstName[MAX_NAME_ARRAY_LENGTH], lastName[MAX_NAME_ARRAY_LENGTH];

Nakon učitavanja potrebnih podataka, vrši se ulančavanje novog elementa u listu. Nakon 156

ove while petlje, formirana je lista sa zapisima o svim teniserima i njihovim poenima na ATP 157

listi. 158

1http://www.cplusplus.com/reference/cstdio/feof/2http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong

5

Page 7: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.4: Kopiranje ucitanih stringova u iz prihvatnih promenljivih(tzv. baferi) u odgovarajuca polja strukture

1 /* Copying string content from buffers firstName and lastName intocorresponding fields of the element structure. */

2 strcpy(newElem->firstName, firstName);3 strcpy(newElem->lastName, lastName);

Potom, prelazi se na drugi deo u kome se vrši modifikacija broja bodova na osnovu sadržaja 159

druge datoteke. Ponovo, učitava se sadržaj datoteke, red po red, svaki se analizira, pronalazi se 160

teniser na koga se podaci iz tekućeg reda odnose, i njegovi poeni se postavljaju na odgovarajuću 161

vrednost. Kôd koji realizuje ovaj korak obrade izdvojen je u listingu 1.5. Ukoliko je teniser čiji 162

su podaci učitani iz tekućeg reda datoteke na ATP listi, promenljiva current će pokazivati na 163

element koji predstavlja tog tenisera, nakon završetka while petlje, kojom se realizuje pretraga 164

po šifri. Nakon toga, broj poena će biti promenjen za odgovarajući iznos, kako je opisano u 165

postavci zadatka. 166

Sada je evidentno da za rešavanje postavljenog problema nije bilo potrebe da se sadržaj druge 167

datoteke učitava. Pored većeg obima posla koji bi pritom bilo potrebno obaviti i nepostojanja 168

nikakvog korisnog efekta od učitavanja i druge datoteke u listu, još jedna loša strana ovakve 169

odluke je nepotrebno zauzeće memorije, što bi trebalo izbeći ukoliko ne postoji stvarna potreba 170

za tim. 171

Listing 1.5: Citanje uspeha tenisera na Wimbledon-u uz ažuriranje poena naATP listi

1 while(fscanf(input2, "%d%d%d", &playerId, &pointsToDefend, &pointsWon)!=EOF) {2 current = head;3 while (current != NULL && current->playerId != playerId)4 current = current->next;5 if (current != NULL)6 current->points += (pointsWon - pointsToDefend);7 }

Kada je ovaj korak kompletiran, može se pristupiti formiranju izlazne datoteke, pošto su 172

sada svi potrebni podaci formirani. Kôd prikazan u listingu 1.6 generiše po jedan red za svakog 173

od tenisera (predstavljenog jednim elementom liste), po istom formatu kao što je slučaj sa 174

ulaznim podacima (opisano u postavci zadatka). 175

Nakon ispisa, preostaje da se dealocira zauzeta memorija, i da se zatvore datoteke koje su 176

otvorene. 177

Listing 1.6: Formiranje izlazne datoteke

1 current = head;2 while (current != NULL) {3 fprintf(izlaz,"%d %s %s %d\n", current->playerId, current->firstName,4 current->lastName, current->points);5 current = current->next;6 }

Još jedanput napominjemo da je korektno pretpostaviti da sadržaj datoteke odgovara for- 178

matu koji je opisan u zadatku, i da nije potrebno proveravati uspešnost konverzija, niti obrađi- 179

vati situaciju u kojoj je red duži nego što je to formatom propisano. 180

6

Page 8: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

1.1.3 Drugo rešenje 181

U listingu 1.7 dat je prikaz još jednog rešenja, koje je realizovano putem funkcija.. 182

7

Page 9: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.7: Alternativno rešenje zadatka183

1 #include <stdio.h> 184

2 #include <stdlib.h> 185

3 #include <string.h> 186

4 187

5 typedef struct elem { 188

6 int playerId; 189

7 char firstName[31]; 190

8 char lastName[31]; 191

9 int points; 192

10 struct elem *next; 193

11 } Elem; 194

12 195

13 /* 196

14 Prototypes of functions being used within the solution. 197

15 Must be specified before the function invocation. 198

16 */ 199

17 // reads data representing player’s information from the file 200

18 Elem* readPlayerData(FILE* atpF); 201

19 202

20 // inserts the element within the list 203

21 Elem* insert(Elem* head, Elem* newElement); 204

22 205

23 // updates players scores, according to the information 206

24 // written in the tournamentFile 207

25 void updatePlayersScore(Elem* list, FILE* tournamentFile); 208

26 209

27 210

28 int main () { 211

29 Elem *head = NULL, *tail = NULL, // first and last list elements 212

30 *player, *current; // auxiliary pointers 213

31 FILE *input1, *input2, *output; 214

32 215

33 if ((input1 = fopen("teniseri.txt","r")) == NULL) { 216

34 printf("Greska u otvaranju ulazne datocurrente teniseri.txt!"); 217

35 exit(1); 218

36 } 219

37 if ((input2 = fopen("wimbledon.txt","r")) == NULL) { 220

38 printf("Greska u otvaranju ulazne datocurrente wimbledon.txt!"); 221

39 exit(1); 222

40 } 223

41 if ((output = fopen("atplist.txt","w")) == NULL) { 224

42 printf("Greska u otvaranju outputne datocurrente atplist.txt!"); 225

43 exit(4); 226

44 } 227

45 228

46 while((player = readPlayerData(input1))!=NULL) { 229

47 head = insert(head, player); 230

48 } 231

49 updatePlayersScore(head, input2); 232

50 233

51 current = head; 234

52 235

53 while (current != NULL) { 236

54 fprintf(output,"%d %s %s %d\n", current->playerId, current->firstName, 237

current->lastName, current->points); 238

55 current = current->next; 239

56 } 240

57 241

58 current = head; 242

8

Page 10: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

59 while (current != NULL) { 243

60 tail = current; 244

61 current = current->next; 245

62 free(tail); 246

63 } 247

64 248

65 fclose(input1); 249

66 fclose(input2); 250

67 fclose(output); 251

68 return 0; 252

69 } 253

70 254

71 Elem* readPlayerData(FILE* atpF) { 255

72 char firstName[31], lastName[31]; 256

73 int playerId, points; 257

74 258

75 if(fscanf(atpF, "%d%s%s%d", &playerId, firstName, lastName, &points)!=EOF) { 259

76 Elem* newElement = malloc(sizeof(Elem)); 260

77 if(newElement==NULL) { 261

78 printf("Greska pri alociranju!"); 262

79 exit(1); 263

80 } 264

81 newElement->playerId = playerId; 265

82 newElement->points = points; 266

83 strcpy(newElement->firstName, firstName); 267

84 strcpy(newElement->lastName, lastName); 268

85 return newElement; 269

86 } 270

87 return NULL; 271

88 } 272

89 273

90 Elem* insert(Elem* head, Elem* newElement) { 274

91 Elem* current = head; 275

92 // adding element to the end of the list 276

93 if(current){ 277

94 while(current->next) 278

95 current = current->next; 279

96 current->next = newElement; 280

97 } 281

98 newElement->next = NULL; 282

99 return head ? head : newElement; 283

100 } 284

101 285

102 void updatePlayersScore(Elem* list, FILE* tournamentFile) { 286

103 int playerId, pointsToDefend, pointsWon; 287

104 while(fscanf(tournamentFile, "%d%d%d", &playerId, &pointsToDefend, &pointsWon) 288

!=EOF) { 289

105 Elem* current = list; 290

106 while (current != NULL && current->playerId != playerId) 291

107 current = current->next; 292

108 293

109 if (current != NULL) 294

110 current->points += (pointsWon - pointsToDefend); 295

111 } 296

112 } 297298

9

Page 11: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Zadatak 1.2 Jun 2012 - zadatak 1 299

Tekstualna datoteka kalendar.txt u svakom redu sadrži informacije o zakazanim aktiv- 300

nostima u jednom danu. Za svaku aktivnost navodi se vreme početka, vreme završetka i kratak 301

opis. Format jednog reda datoteke je dat u priloženom primeru. Napisati program na program- 302

skom jeziku C koji čita red po red iz datoteke, pravi strukturu aktivnosti na osnovu podataka 303

iz pročitanog reda i stavlja je u listu u hronološkom poretku. Potom, polazeći od početka liste 304

pronalazi aktivnosti koje se preklapaju. Ako se detektuje preklapanje između dve aktivnosti, iz 305

liste se izbacuje aktivnost koja počinje kasnije ili, ako obe počinju u isto vreme, izbacuje se ona 306

koja je druga u poretku. Aktivnost se može preklapati sa proizvoljno mnogo drugih aktivnosti. 307

Aktivnosti koje se izbace iz liste, ispisuju se u izlaznu tekstualnu datoteku preklapanja.txt. Za 308

kratak opis aktivnosti koristi se maksimalno 80 znakova. Na kraju program treba da ispravno 309

oslobodi zauzete resurse. Učitavanje i brisanje liste realizovati kao potprograme. 310

08:00-10:30 Ispit iz Osnova računarske tehnike.10:00-10:30 Pregled kod doktora.11:00-11:15 Preuzeti skripte iz štamparije.09:00-09:30 Odbrana domaćeg iz Praktikuma iz programiranja 2.

Tabela 1.1: Primer ulaza (kalendar.txt)

09:00-09:30 Odbrana domaćeg iz Praktikuma iz programiranja 2.10:00-10:30 Pregled kod doktora.

Tabela 1.2: Primer izlaza (preklapanja.txt):

Kompletan kod dat je u listingu 1.25. 311

Rešenje demonstrira nekoliko bitnih komponenti i tehnika u rešavanju zadataka: 312

• Slojevito pisanje programa 313

• Rad sa tekstualnom datotekom 314

• Različite metode učitavanja podataka iz datoteke 315

• Mehanizam obrade neželjenih situacija 316

• Pravilno rukovanje dinamičkom memorijom 317

1.2.1 Analiza Problema 318

Problem se može razložiti na sledeće potprobleme: 319

• Otvaranje ulazne i izlazne datoteke 320

• Provera mogućnosti rada sa datotekama 321

• Učitavanje sadržaja datoteke, pri čemu se pročitani sadržaj transformiše u odgovarajuću 322

strukturu podataka i dodaje u listu 323

Svaki red datoteke odgovara jednom podatku, pa se formira po jedan objekat strukture 324

Activity za svaki učitani podatak. Ovaj objekat se formira dinamički. 325

Ulančavanje se vrši tako da se aktivnosti predstavljene elementima liste smeštaju tako 326

da postoji jasan poredak između njih: aktivnost koja počinje ranije u listi se mora i nalaziti 327

ranije. 328

10

Page 12: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

• Centralna obrada - uklanjanje termina sa poklapanjem uz njihov ispis. Neophodno je 329

obraditi sadržaj učitanih termina, ustanoviti da li postoji preklapanje nekih aktivnosti, i 330

ukoliko postoji par (a1, a2), gde a1 započinje ranije, ispisati podatke o a2, a potom ga i 331

ukloniti. 332

Tokom rešavanja zadatka, neophodno je voditi računa o svim ovde navedenim stavkama. 333

Rešavanje određenih koraka biće ostvareno kroz potprograme (funkcije), a neki će biti rešeni 334

direktno (odmah navodeći kôd). 335

1.2.2 Strukture podataka 336

Pre pristupanja pisanju rešenja, potrebno je ustanoviti kakve su strukture podataka po- 337

trebne. Budući da se aktivnost karakteriše vremenom početka i završetka, pogodno je uvesti 338

strukturu koja predstavlja vreme – Time, i sastoji se od komponenti koje opisuju sate i minute. 339

Po prirodi problema, sati i minuti su celobrojne veličine. 340

Listing 1.8: Struktura za opis vremena

1 typedef struct sTime {2 int hours, minutes;3 } Time;

Bilo bi nepogodno raditi bez ovakve strukture, budući da je svaka aktivnost opisana počet- 341

nim i krajnjim vremenom, te bi trebalo uvesti 4 promenljive. Na ovaj način, vreme je opisano 342

jednim podatkom, što čini program čitljivijim i razumljivijim. 343

Zatim, potrebno je definisati strukturu koja predstavlja samu aktivnost. Pored polja koja 344

opisuju početno i krajnje vreme, i koja su po tipu Time, definiše se i opis, za koji je rečeno da je 345

dugačak najviše 80 karaktera. Kako nam je poznato da opis neće biti duži od 80, opredeljujemo 346

se za niz od 81 karaktera (80 učitanih i 1 za NULL karakter).Tehnički, budući da se ovde definiše 347

strukturni tip, moguće je bilo izostaviti identifikator sTime. 348

Listing 1.9: Struktura za opis aktivnosti

1 typedef struct sActivity {2 Time startTime, endTime;3 char description[DEFAULT_TEXT_LEN + 1];4 } Activity;

Potom, kako je zadatak neophodno rešiti koristeći ulančanu listu, definiše se struktura koja 349

predstavlja jedan element liste. Polje pActivity unutar te strukture je pokazivač na objekat 350

aktivnosti na koju se odnosi. 351

Listing 1.10: Struktura elementa liste

1 typedef struct sListElement {2 Activity * pActivity;3 struct sListElement * next;4 } ListElement;

Na ovom mestu, moguće je primeniti jedno od dva alternativna rešenja: da element li- 352

ste sadrži pokazivač na aktivnost, kao što je to ovde slučaj, ili da sadrži aktivnost direktno: 353

11

Page 13: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Activity activity. Generalno, boljim izborom se smatra rešenje u kome je informacioni 354

sadržaj elementa liste predstavljen pokazivačem umesto da se sadrži u elementu liste direktno. 355

Opravdanje za ovo stanovište leži u činjenici da lista predstavlja kolekciju objekata, uređenih na 356

određeni način (ili neuređenih), koji mogu postojati i izvan elementa liste – aktivnost ne mora 357

biti ulančana da bi postojala; takođe, jedna aktivnost može biti u dve ulančane liste: jednoj 358

koja sadrži aktivnosti u okviru pojedinačnih dana, drugoj koja sadrži sve aktivnosti itd. 359

Sa druge strane, pored prednosti, ovaj pristup ima osobine koje se mogu smatrati nedosta- 360

cima: potreban je dodatni memorijski prostor potreban za smeštanje pokazivača na informaci- 361

oni sadržaj, moguće je da pokazivač ima vredenost NULL, pa je potrebno vršiti proveru da li 362

pokazivač na podatke ima vrednost NULL itd. 363

Oba ovde opisana pristupa smatraju se korektnim za upotrebu, s tim što prvi pristup, u 364

kome je informacioni sadržaj direktno smešten u okviru strukture elementa liste, nije uvek 365

zadovoljavajući. 366

1.2.3 Definisanje koraka u rešenju 367

Sada možemo krenuti sa pisanjem programa. Najbolji metod je razmišljati “u koracima”, i 368

krenuti od pisanja glavnog programa. Koraci koji se pritom preduzimaju su: 369

• Otvaranje datoteka; 370

• Provera mogućnosti rada sa datotekom (uspešnost otvaranja datoteke); 371

• Repetitivno učitavanje podatka o jednoj aktivnosti (pri čemu se zapravo iz ulazne datoteke 372

pročita po jedan red) i smeštanje tih podataka u objekat tipa Activity 373

• Ulančavanje učitane aktivnosti, uz održavanje uređenosti – potrebno je smestiti novu 374

aktivnost tako da svi njeni prethodnici u listi započinju pre nje ili istovremeno sa njom, a 375

svi njeni sledbenici nakon nje. Ovo sugeriše da će biti potrebno vršiti poređenje vremena 376

početka dveju aktivnosti; 377

• Prelazak na obradu učitanih aktivnosti (pronalaženje preklapanja i uklanjanje preklo- 378

pljenih aktivnosti koje započinju kasnije), nakon što se pročitaju svi podaci iz ulazne 379

datoteke. Pri proveri preklapanja, biće neophodno porediti vremena početka i završetka 380

dveju aktivnosti. 381

• Zatvaranje otvorenih datoteka; 382

• Oslobađanje memorije zauzete za učitane podatke. 383

Prilikom pisanja kôda rešenja, svaki “krupniji” korak, u kome se ostvaruje nekakva obrada 384

nad podacima, može se izdvojiti u zasebnu funkciju. Ovaj postupak se naziva razvoj programa 385

u koracima preciziranja i ime se postižu dve stvari: 386

• Kôd postaje razumljiviji za čitanje i razumevanje; 387

• Prilikom pisanja koda, nastavlja se sa pisanjem naredbi kojima se ostvaruje tekući cilj, 388

umesto da se prelazi na pisanje naredbi koje vrše sekundarne akcije (npr. provere, kalku- 389

lacije i sl.). 390

Drugi cilj je značajan, jer se na ovaj način ne gubi iz vida šta je potrebno u osnovnom 391

problemu rešiti, i time se sprečavaju greške koje nastaju zbog pada koncentracije ili previda. 392

Dakle, dok se piše kôd kojim se rešava osnovni problem, pozivaju se (unapred) funkcije 393

koje još uvek nisu napisane. Tada je bitno shvatiti kakve je akcije potrebno obaviti (npr. 394

12

Page 14: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

poređenje vremena početka dve aktivnosti), odlučiti se da se te akcije obave putem funkcije 395

(ma koliko trivijalno možda delovale) i odlučiti se za način dobavljanja rezultata obrade od 396

te funkcije (povratnom vrednošću ili prenosom argumenata po adresi). Nakon što je kôd koji 397

rešava osnovni problem kompletiran, može se pristupiti pisanju pozvanih funkcija, u skladu sa 398

odlukama donetim u momentu kada je uočena potreba za njima. 399

1.2.4 Glavni program 400

Postupak učitavanja realizuje se putem while petlje: 401

Listing 1.11: Ucitavanje i ulancavanje aktivnosti

1 while ((currActivity = readActivity(scheduleFile)) != NULL) {2 activityList = insert(activityList, currActivity);3 }

Kako je potrebno svaki red iz ulazne datoteke učitati, postupak se mora vršiti u petlji. 402

Pritom, u svakom koraku petlje, iz datoteke je potrebno učitati novu aktivnost i napraviti 403

dinamički alociranu Activity strukturu. Zbog toga je uslov petlje sačinjen od poziva funkcije 404

readActivity, koja iz datoteke scheduleFile učitava podatke zapisane u jednom redu. 405

Na ovom mestu nije napisan kôd koji ostvaruje učitavanje, već je doneta odluka da se taj 406

posao delegira nekoj funkciji readActivity. Ta funkcija će kasnije biti napisana i objašnjena. 407

Trenutno, bitno je odlučiti se na osnovu čega će funkcija obezbediti novu aktivnost (na osnovu 408

jednog reda datoteke – predaje joj se argument koji predstavlja pokazivač na FILE strukturu, 409

tj. datoteku), i šta će biti njen rezultat (pokazivač na novoalocirani objekat koji predstavlja 410

aktivnost). Slično je učinjeno i sa funkcijom insert, koja učitanu aktivnost (currActivity) 411

ulančava u listu activityList. 412

Naredni korak koji se ostvaruje u main funkciji je obrada učitanih podataka. To se ne čini 413

direktno u main funkciji, već se ponovo delegira nova funkcija koja će vršiti tu obradu. 414

Listing 1.12: Poziv centralne obrade

1 printActivityList(activityList, stdout);2 removeOverlappedActivities(activityList, overlapingFile);3 printActivityList(activityList, stdout);

Kompletna funkcija main izgleda kao u listingu 1.13. 415

1.2.5 Implementiranje pozivanih funkcija 416

Kada su osnovni koraci objašnjeni, potrebno je napisati funkcije koje ostvaruju prethodno 417

usvojene korake. U listingu 1.14 prikazana je funkcija koja obavlja učitavanje podataka o 418

pojedinačnim aktivnostima. 419

Unutar funkcije, alocira se aktivnost koja se smešta u promenljivu actv. Potom, u po- 420

lja actv->startTime.hours, actv->startTime.minutes, actv->endTime.hours, 421

actv->endTime.minutes se smeštaju podaci o početku i završetku aktivnosti - to su brojevi 422

razdvojeni znacima : i -, kako je dato u primeru ulazne datoteke. Zbog toga, format-string za 423

funkciju scanf izgleda ovako: %d:%d-%d:%d. 424

Nakon učitavanja numeričkih podataka, potrebno je obaviti proveru njene uspešnosti. Uko- 425

liko je povratna vrednost funkcije fscanf 3 jednaka konstanti EOF, to je indikacija da se 426

3Povratna vrednost za fscanf http://www.cplusplus.com/reference/cstdio/fscanf/#return

13

Page 15: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.13: Glavni program

1 int main() {2 ListElement* activityList = NULL; Activity* currActivity;3 int s = 0;4 FILE* scheduleFile = fopen("kalendar.txt", "r");5 FILE* overlapingFile = fopen("preklapanja.txt", "w");6

7 if (scheduleFile == NULL) {8 fprintf(stderr, "Cannot open input file."); s = 1; goto END;9 }

10 if (overlapingFile == NULL) {11 fprintf(stderr, "Cannot open output file."); s = 1; goto END;12 }13

14 while ((currActivity = readActivity(scheduleFile)) != NULL) {15 activityList = insert(activityList, currActivity);16 }17

18 printActivityList(activityList, stdout);19 removeOverlappedActivities(activityList, overlapingFile);20 printActivityList(activityList, stdout);21

22 END: if (scheduleFile) fclose(scheduleFile);23 if (overlapingFile) fclose(overlapingFile);24

25 dealocate(activityList);26 return s;27 }

Listing 1.14: Ucitavanje jedne aktivnosti

1 Activity* readActivity(FILE* f) {2 int numOfData;3 Activity* actv = (Activity*)malloc(sizeof(Activity));4 if(!actv) {5 printf("Error in memory allocation");6 exit(2);7 }8 numOfData = fscanf(f, "%d:%d-%d:%d ",9 &actv->startTime.hours, &actv->startTime.minutes,

10 &actv->endTime.hours, &actv->endTime.minutes);11

12 if (numOfData == EOF) {13 free(actv); //nepotrebno smo alocirali14 return NULL;15 }16

17 fgets(actv->description, DEFAULT_TEXT_LEN, f);18

19 return actv;20 }

14

Page 16: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

čitanjem došlo do kraja datoteke. Tada je potrebno osloboditi memoriju zauzetu za aktivnost 427

(actv), jer neće biti iskorišćena, a korisniku se vraća NULL kao rezultat – ne postoji nikakav 428

validan podatak koji se može vratiti. 429

Poslednji korak u učitavanju je dobavljanje teksta koji predstavlja kratak opis aktivnosti. 430

Budući da se radi o tekstu koji se proteže do kraja linije, može se učitati pomoću funkcije 431

fgets4, pozivom fgets(actv->description,DEFAULT_TEXT_LEN,f). Ta funkcija pri- 432

hvata adresu od koje se započinje smeštanje učitanog sadržaja, maksimalni broj karaktera koji 433

se može učitati i datoteku iz koje se čita. Potrebno je napomenuti da ova funkcija zadržava znak 434

za prelaz u novi red (\n), a nakon njega dodaje znak za kraj stringa (\0). Međutim, ukoliko je 435

linija koja se učitava duža od prihvatnog stringa, učitaće maksimalni broj karaktera, a nakon 436

toga će dodati znak kraj stringa. 437

Pri rešavanju zadataka, može se smatrati da je sadržaj ulazne datoteke uvek u skladu sa 438

opisanim formatom, pa se provera ne mora vršiti! Iz tog razloga, u listingu 1.14 nije navedena 439

nijedna provera o korektnosti učitanih podataka – ni da li su svi potrebni podaci učitani (da 440

li je 4 cela broja uspešno pročitano ili nije), ni da li komponente vremena imaju odgovarajuće 441

vrednosti (minuti u opsegu 0-59, sati 0-23), i oslanjalo se na pretpostavku da je dužina linije 442

najviše 80 karaktera (nisu vršene dodatne aktinvosti koje obezbeđuju da se učita čitava linija). 443

Radi kompletnosti, u nastavku se daje diskusija obrade ovih izuzetnih situacija, koja se pri 444

rešavanju ispitnih zadataka ne mora obavljati. 445Pri čitanju podataka funkcijom fscanf, moguće je da dođe do greške ako ulazni podaci ne zadovoljavaju format koji je 446

zadat. Na primer, ako linija sadrži 9:5-xy:20 P2 konsultacije, tada će fscanf prekinuti čitanje kada dođe do karaktera 447x, i biće vraćena vrednost 2 (jer su 9 i 5 uspešno konvertovani podaci). Međutim, naredno čitanje funkcijom fscanf, ili bilo 448kojom drugom funkcijom, nastaviće se od karaktera x – taj karakter je doveo do prekida prethodnog učitavanja, i smatra 449se neiskorišćenim. Ovde bi bilo potrebno proveriti povratnu vrednost funkcije fscanf, i ukoliko je manja od 4, prikazati 450poruku o problemu. Ukoliko bi se nastavila obrada (zanemarivši loše formiranu liniju ulaznog fajla), potrebno bi bilo preći 451na narednu liniju. Budući da je naredni korak učitavanje ostatka tekuće linije (kratak opis aktivnosti), ostatak tekuće 452(neavalidne) linije biće pročitan, i preći će se na narednu liniju. Međutim, ova funkcija ne bi trebalo da vrati pozivaocu 453strukturu formiranu na osnovu nevalidnih podataka, pa bi bilo ispravno da se u ovoj situaciji oslobodi memorija zauzeta 454za aktivnost (kao u slučaju greške pri čitanju), i da se vrati NULL. U delu 1.2.7, listing 1.28 je prikazano još jedno rešenje 455ovog problema, u kome se na efikasniji način obezbeđuje učitavanje teksta do završetka linije. 456

U listingu 1.15 prikazujemo kôd kojim se obezbeđuje da se čitava linija učita do kraja (do znaka za novi red), u slučaju 457da je duža od formatom propisanih 80 karaktera. Ukoliko se desi da je linija duža od 80 karaktera, mora se “preskočiti” 458ostatak, kako bi čitanje narednog validnog seta podataka započelo u narednoj liniji (u suprotnom, učitavanje nastavlja na 459mestu gde je prethodno učitavanje prekinuto). 460

Nakon funkcije za unos strukture, potrebno je obezbediti funkciju koja će dodati novi ele- 461

ment koji predstavlja aktivnost u listu aktivnosti. To se postiže funkcijom insert, prikazanoj u 462

listingu 1.16. 463

Kako je potrebno da se sadržaj liste uvek održava uređenim, tako da aktivnost koja na nižoj 464

poziciji u listi ranije započinje nego neka druga, pri umetanju se prvo pronalazi mesto na koje bi 465

se umetnula nova aktivnost. Stoga, potrebno je ispitivati početno vreme svakog elementa liste 466

– curr i uporediti sa vremenom početka nove aktivnosti – actv. Novu aktivnost umećemo 467

u listu na poziciju za koju važi da su sve aktivnosti koje su u listi “ispred” počinju ranije ili 468

istovremeno sa njom, a sve koje su u listi “iza” počinju nakon nje. 469

Ukoliko je aktivnost na koju se odnosi element curr pre aktivnosti na koju se odnosi actv, 470

potrebno je nastaviti kretanje kroz listu. Tek kada se u listi dođe do prve aktivnosti koja ne 471

započinje pre aktivnosti actv (ili istovremeno sa njom), kretanje kroz listu se zaustavlja, a 472

curr pokazuje na element koji se odnosi na prvu aktivnost koja počinje nakon početka actv. 473

Dakle, aktivnost actv se dodaje ispred elementa curr. 474

Proveru koja aktivnost je “ranija” možemo obaviti direktno u uslovu grananja. Međutim, 475

praktičnije je upotrebiti poziv funkcije koja radi ovo poređenje: 476

isActivityBeforeOrEqual(curr->pActivity,actv) 477

Gledano sa praktične strane pri rešavanju zadatka, ne menja se fokus razmišljanja – i dalje 478

4http://www.cplusplus.com/reference/cstdio/fgets/

15

Page 17: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.15: Ucitavanje citave linije (dopuna funkciji readActivity)

1 // ... Prethodno ucitani podaci preko fscanf ...2 // Ucita se opis aktivnosti3 fgets(actv->description, DEFAULT_TEXT_LEN, f);4

5 if(strlen(actv->description) == DEFAULT_TEXT_LEN6 && actv->description[DEFAULT_TEXT_LEN-1] != ’\n’){7 /* procitano je tacno 80 karaktera, i poslednji nije znak za prelaz u novi

red,8 sto znaci da postoji jos karaktera do prelaska u novi red. */9 while(1){

10 /* prihvatni niz znakova koji se zanemaruju - ostatak linije */11 char tmp[DEFAULT_TEXT_LEN+1];12 /* Ucita se jos najvise DEFAULT_TEXT_LEN karaktera, ili do pojave znaka \n */13 fgets(tmp, DEFAULT_TEXT_LEN, f);14

15 /* Ukoliko je ucitano manje od DEFAULT_TEXT_LEN karaktera, ili je ucitano16 toliko, ali poslednji je jednak znaku za prelaz u novi red,17 zakljucujemo da su procitani svi znaci koji postoje u tom redu i da se ova18 petlja moze napustiti. */19 if(strlen(tmp) < DEFAULT_TEXT_LEN || tmp[DEFAULT_TEXT_LEN-1] == ’\n’) break;20 }21 }22 //...23

Listing 1.16: Umetanje aktivnosti u listu

1 ListElement* insert(ListElement* lstFirst, Activity* actv) {2 ListElement* newActv = (ListElement*) malloc(sizeof(ListElement));3

4 newActv->pActivity = actv;5

6 ListElement* curr = lstFirst, *prev = NULL;7 while (curr) {8 if (isActivityBeforeOrEqual(curr->pActivity, actv))9 prev = curr, curr = curr->next;

10 else break; // pronasli smo mesto!11 }12

13 if (!prev) lstFirst = newActv; //prvi element14 else prev->next = newActv; //15 newActv->next = curr; // ulancavanmo u postojecu16

17 return lstFirst; // vracamo glavu liste18 }

16

Page 18: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

se razmišlja o tome kako se ulančava novi element u listu, dok se samo implementiranje provere 479

(koja može biti komplikovana!) ostavlja za kasnije, čime se umanjuju šanse da se nešto previdi 480

pri samom ulančavanju, na čemu bi trebalo da bude glavni fokus u ovom trenutku. 481

Ilustracije radi, provera koja bi ustanovila da li postoji bilo kakvo poklapanje bi glasila kao 482

u listingu 1.17. Napor potreban da se analizira postojanje preklapanja, i vreme potrebno za to, 483

mogu uticati na loše napisan kod za umetanje elementa, pa je zato delegiranje provere novoj 484

funkciji u ovom koraku najbolje rešenje. Štaviše, ako se ponovo pogleda analiza problema, može 485

se shvatiti da će poređenje vremena početka aktivnosti biti potrebno na još nekom od mesta 486

(prilikom izbacivanja, v. 1.2.3), pa je i sa te strane uputno da se ovakva provera ostvari u 487

funkciji koja se ovde jednostavno poziva. 488

Listing 1.17: Direktno napisana provera preklapanja

1 if(a1->startTime.hours < a2->startTime.hours ||2 (a1->startTime.hours==a2->startTime.hours &&3 a1->startTime.minutes < a2->startTime.minutes)) //...

Nakon umetanja, potrebno je realizovati i funkciju za uklanjanje aktivnosti koje se prekla- 489

paju; to je funkcija removeOverlappedActivities, koja se poziva u main-u, i čiji kôd je 490

prikazan u listingu 1.18. Zahtev zadatka je da se za aktivnosti koje se preklapaju uklone sve 491

osim aktivnosti sa najmanjim početnim vremenom (prva u posmatranom skupu preklopljenih). 492

Da bi se uklonila sva poklapanja, kroz listu se prolazi sa dva pokazivača. Prvi pokazivač – 493

pi pokazuje na element čiji se duplikati traže; drugi pokazivač – pj koristi se da se, za fiksirano 494

pi, ispitaju svi elementi koji su u listi posle njega (aktivnosti koje počinju kasnije). Ukoliko se 495

aktivnost na koju se odnosi element pj poklapa sa aktivnošću predstavljenu elementom pi, tada 496

je potrebno ukloniti element pj. Prvo se štampa aktivnost koja se izbacuje iz liste, a potom 497

se radi prevezivanje pokazivača i dealocira memorija koju je zauzimala, a potom se element 498

uklanja iz liste. 499

Listing 1.18: Uklanjanje preklopljenih aktivnosti

1 ListElement* removeOverlappedActivities(ListElement* aList, FILE* output) {2 ListElement * pi = aList, * pj = NULL, * pjPrev, * old;3 while (pi) {4 pj = pi->next;5 while (pj) {6 if (isOverlaped(pi->pActivity, pj->pActivity)) {7 printActivity(pj->pActivity, output);8 pi->next = pj->next;9 free(pj->pActivity);

10 free(pj);11 pj = pi->next;12 } else {13 pj = pj->next;14 }15 }16 pi = pi->next;17 }18

19 return aList;20 }

Pri dealociranju memorije, jako je važno obratiti pažnju na to da li je struktura sa poda- 500

17

Page 19: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

cima dinamički alocirana i element liste sadrži pokazivač na tu sturkuturu ili je sastavni deo 501

strukture elementa liste (nije pokazivačko polje). Pošto je u ovom rešenju aktivnost alocirana di- 502

namički (Element sadrži Activity* actv, a ne Activity actv), neophodno je osloboditi 503

memoriju koju zauzima aktivnost, a tek potom i memoriju koju zauzima element liste. 504

Za pokazivač pj nije neophodno da se sa početka liste radi ispitivanje duplikata – nemoguće 505

je da ispred pi postoji preklopljeni termin (pk), inače bi pi već bio uklonjen, kao termin koji 506

se preklapa sa pk. 507

I u ovom slučaju, proveru postojanja preklapanja delegiramo funkciji isOverlapped, koja 508

za dve zadate aktivnosti proverava da li se preklapaju, budući da je za proveru preklapanja 509

aktivnosti potrebno utvrditi kakav je odnos između perioda (početno i završno vreme) te dve 510

aktivnosti. 511

Pre nastavka, potrebno je prodiskutovati deo koda u kome se prevezuju elementi liste. 512

Naime, prevezivanje se ostvaruje naredbom pi->next=pj->next. Može delovati da je ovo 513

pogrešno, jer pi ne mora biti u listi neposredno ispred pj. Međutim, ovo upravo jeste slučaj! 514

Ako ovu funkciju koristimo samo za obradu liste aktivnosti kod koje se aktivnosti koje se 515

preklapaju nalaze neposredno jedna ispred druge (ne postoji aktivnost koja je između neke dve 516

preklopljene aktivnosti, a da se ne preklapa sa ranijom aktivnošću), ovakav pristup je korektan. 517

Naime, kako je lista aktivnosti vremenski uređena, sve aktivnosti koje se mogu poklopiti 518

sa aktivnošću pi u listi se nalaze isključivo iza pi. Prema tome, kada se uklanja prva ak- 519

tivnost pj koja se poklapa sa pi (a započinje kasnije), ona se nalazi neposredno iza pi, t.j. 520

pi->sled == pj. Ako postoji još jedna aktivnost koja se preklapa sa pi, osim pj, ona mora 521

slediti aktivnost pj, t.j. to je pj->next. Ukoliko pj->next ne preklapa sa pi, to znači da 522

ne postoji nijedna druga aktivnost preklopljena sa pi. Ako se pj->next poklapa sa pi, ona 523

će biti uklonjena već u narednoj iteraciji, a njen prethodnik u listi će biti opet pi. Važno je 524

uočiti da ova funkcija nikada neće ostaviti listu praznom, tačnije, element koji je prvi u listi 525

nikada neće biti izbačen! 526

Alternativno, ukoliko se ovo svojstvo ne uoči, generalni postupak uklanjanja elementa (v. 527

listing 1.19.) se može sprovesti, uvek prateći i evidenciju o tome koji je element prethodnik 528

onom elementu koji se potencijalno izbacuje (pret). 529

Listing 1.19: Uklanjanje preklopljenih elemenata bez uocene specificnosti

1 Elem* pret = NULL;2 while(pi){3 pj = pi->next;4 pret = pi;5 while(pj){6 if(isOverlapped(p1->aActivity, pj->pActivity)){7 printActivity(pj->activity, ouput);8 pret->next = pj->next; // pret!=NULL uvek, zbog pret=pi;9 free(pj->pActivity);

10 free(pj);11 }12 else{13 pret = pj; pj = pj->next; // pracenje prethodnika14 }15 }16 pi = pi->next;17 }

Sada je preostalo još analizirati funkcije koje su pozivane pri pisanju do sada analiziranih 530

funkcija. To su 531

18

Page 20: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

• isActivityBeforeOrEqual, koja poredi dve aktivnosti i vraća 1 u slučaju da aktiv- 532

nost predstavljena prvim argumentom počinje pre ili istovremeno sa aktivnošću predsta- 533

vljenom drugim argumentom. 534

• isOverlaped, koja poredi dve aktivnosti i vraća 1 ukoliko postoji prekpalanje u inter- 535

valima trajanja tih aktivnosti 536

• printActivity, koja je pozivana pre izbacivanja aktivnosti iz liste. 537

Budući da se provera da li je jedna aktivnost pre druge svodi na poređenje početnih vremena, 538

i na ovom mestu se ta provera ostvaruje poredeći da li je podatak koji predstavlja startno vreme 539

prve aktivnosti manji ili jednak startnom vremenu druge. 540

Listing 1.20: Provera da li je aktivnost pre druge zadate aktivnosti

1 int isActivityBeforeOrEqual(Activity* a1, Activity* a2) {2 return isTimeBeforeOrEqual(a1->startTime, a2->startTime);3 }

Preklapanje između dve aktivnosti se proverava na sledeći način: dve aktivnosti se prekla- 541

paju ukoliko aktivnost koja počinje kasnije ima početno vreme veće od završnog vremena one 542

aktivnosti koja počinje ranije. Bilo koja od zadate dve aktivnosti može biti ona koja počinje 543

ranije. Pošto je potrebno uraditi istu proveru odnosa između vremena početka i završetka ak- 544

tivnosti, poređenje odnosa je takođe realizovano funkcijama. Leva strana operatora || ispituje 545

da li ima preklapanja u slučaju da je a1 ranija aktivnost (počinje pre a2 ili istovremeno sa 546

njom), a desna strana tog operatora ispituje postojanje preklapanja ako je a2 ranija aktivnost. 547

Listing 1.21: Provera postojanja preklapanja zadatih aktivnosti

1 int isOverlaped(Activity* a1, Activity* a2) {2 return ( isTimeBeforeOrEqual(a1->startTime, a2->startTime)3 && isTimeBefore(a2->startTime, a1->endTime) )4 || ( isTimeBeforeOrEqual(a2->startTime, a1->startTime)5 && isTimeBefore(a1->startTime, a2->endTime) );6 }

I ova funkcija je realizovana uvodeći nove funkcije, koje bi trebalo da uporede vremena. 548

Funkcija koja poroverava da li je prvi zadati vremenski trenutak pre drugog data je u listingu 549

1.22, a funkcija koja proverava da li je pre ili istovremeno sa drugim trenutkom u listingu 1.23. 550

Listing 1.22: Poredenje dva vremenska trenutka na strogu nejednakost

1 int isTimeBefore(Time t1, Time t2) {2 return (t1.hours < t2.hours) ||3 (t1.hours == t2.hours && t1.minutes < t2.minutes);4 }

Na kraju, potrebno je osloboditi elemente liste. Funkcija deallocate oslobađa dinamički 551

zauzetu memoriju. Važno je osloboditi i prostor koji zauzima element liste, ali i prostor koji 552

zauzima struktura koja sadrži podatke o aktivnosti, budući da je ona dinamički alocirana. Kôd 553

je dat u listingu 1.24. 554

19

Page 21: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.23: Poredenje dva vremenska trenutka na nejednakost (uz mogucujednakost)

1 int isTimeBeforeOrEqual(Time t1, Time t2) {2 return (t1.hours < t2.hours) ||3 (t1.hours == t2.hours && t1.minutes <= t2.minutes);4 }

Listing 1.24: Oslobadanje elemenata liste

1 void dealocate(ListElement* actv){2 ListElement* tmp;3 while(actv){4 tmp = actv;5 actv = actv->next;6 /* Obavezno osloboditi i memoriju koju zauzima dinamicki alocirana7 struktura koja predstavlja aktivnost! */8 free(tmp->pActivity);9 free(tmp);

10 }11 }

1.2.6 Kompletan program i zaključak 555

U listingu 1.25 dat je kompletan program. 556

Program koji je ovde priložen ilustruje dobre programerske postupke i način organizacije 557

kôda i struktuiranja rešenja, koje bi trebalo usvojiti – podela odgovornosti na više funkcija, 558

formiranje rešenja “odozgo na dole”, imenovanje promenljivih i funkcija tako da njihova namena 559

bude jasna iz samog identifikatora itd. 560

Rešenje, po obimu (163 linije kôda), može delovati preveliko, ali, ako se pažljivije pogleda 561

kôd, veliki deo linija “otpada” na deklaracije funkcija, na prorede (prazne redove) i redove koji 562

sadrže samo vitičaste zagrade. U ovom rešenju je takođe prikazano kako pažljiva analiza pro- 563

blema može da dovede do pojednostavljenja ili optimizovanja pisanog programa, ali je ukazano 564

i da generalni algoritmi mogu da se primene, bez ikakve modifikacije, i da bi se ponašali jednako 565

efikasno. 566

Na kraju, ukazano je na činjenicu da kalkulacije koje naizgled deluju jednostavno (kao 567

što je utvrđivanje preklapanja termina) mogu da postanu komplikovani izrazi, i da upotreba 568

funkcija omogućuje da se takve kompleksne operacije izdvoje izvan osnovne obrade, i da se 569

realizuju kasnije, u više koraka, što pisanje programa čini jednostavnijim, a kôd postaje čitljiviji 570

i razumljiviji. 571

20

Page 22: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.25: Kompletan program572

1 #include <stdio.h> 573

2 #include <stdlib.h> 574

3 #include <string.h> 575

4 #define DEFAULT_TEXT_LEN 80 576

5 577

6 typedef struct sTime { 578

7 int hours, minutes; 579

8 } Time; 580

9 581

10 typedef struct sActivity { 582

11 Time startTime, endTime; 583

12 char description[DEFAULT_TEXT_LEN + 1]; 584

13 } Activity; 585

14 586

15 typedef struct sListElement { 587

16 Activity * pActivity; 588

17 struct sListElement * next; 589

18 } ListElement; 590

19 591

20 ListElement* insert(ListElement* l, Activity* act); 592

21 Activity* readActivity(FILE* f); 593

22 ListElement* removeOverlappedActivities(ListElement* aList, FILE* output); 594

23 void printActivity(Activity* actv, FILE * f); 595

24 void printActivityList(ListElement* aList, FILE* f); 596

25 int isOverlaped(Activity* a1, Activity* a2); 597

26 int isTimeBeforeOrEqual(Time t1, Time t2); 598

27 int isActivityBeforeOrEqual(Activity* a1, Activity* t2); 599

28 int isTimeBefore (Time t1, Time t2); 600

29 int isActivityBefore(Activity* a1, Activity* a2); 601

30 void dealocate(ListElement* actv); 602

31 603

32 int main() { 604

33 ListElement* activityList = NULL; Activity* currActivity; 605

34 int s = 0; 606

35 FILE* scheduleFile = fopen("kalendar.txt", "r"); 607

36 FILE* overlapingFile = fopen("preklapanja.txt", "w"); 608

37 609

38 if (scheduleFile == NULL) { 610

39 fprintf(stderr, "Cannot open input file."); s = 1; goto END; 611

40 } 612

41 if (overlapingFile == NULL) { 613

42 fprintf(stderr, "Cannot open output file."); s = 1; goto END; 614

43 } 615

44 616

45 while ((currActivity = readActivity(scheduleFile)) != NULL) { 617

46 activityList = insert(activityList, currActivity); 618

47 } 619

48 620

49 printActivityList(activityList, stdout); 621

50 removeOverlappedActivities(activityList, overlapingFile); 622

51 printActivityList(activityList, stdout); 623

52 624

53 END: if (scheduleFile) fclose(scheduleFile); 625

54 if (overlapingFile) fclose(overlapingFile); 626

55 627

56 dealocate(activityList); 628

57 return s; 629

58 } 630

59 631

21

Page 23: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

60 Activity* readActivity(FILE* f) { 632

61 int numOfData; 633

62 Activity* actv = (Activity*)malloc(sizeof(Activity)); 634

63 if(!actv) { 635

64 printf("Error in memory allocation"); 636

65 exit(2); 637

66 } 638

67 numOfData = fscanf(f, "%d:%d-%d:%d ", 639

68 &actv->startTime.hours, &actv->startTime.minutes, 640

69 &actv->endTime.hours, &actv->endTime.minutes); 641

70 642

71 if (numOfData == EOF) { 643

72 free(actv); //nepotrebno smo alocirali 644

73 return NULL; 645

74 } 646

75 647

76 fgets(actv->description, DEFAULT_TEXT_LEN, f); 648

77 649

78 return actv; 650

79 } 651

80 652

81 ListElement* insert(ListElement* lstFirst, Activity* actv) { 653

82 ListElement* newActv = (ListElement*) malloc(sizeof(ListElement)); 654

83 655

84 newActv->pActivity = actv; 656

85 657

86 ListElement* curr = lstFirst, *prev = NULL; 658

87 while (curr) { 659

88 if (isActivityBeforeOrEqual(curr->pActivity, actv)) 660

89 prev = curr, curr = curr->next; 661

90 else break; // pronasli smo mesto! 662

91 } 663

92 664

93 if (!prev) lstFirst = newActv; //prvi element 665

94 else prev->next = newActv; // 666

95 newActv->next = curr; // ulancavanmo u postojecu 667

96 668

97 return lstFirst; // vracamo glavu liste 669

98 } 670

99 671

100 ListElement* removeOverlappedActivities(ListElement* aList, FILE* output) { 672

101 ListElement * pi = aList, * pj = NULL, * pjPrev, * old; 673

102 while (pi) { 674

103 pj = pi->next; 675

104 while (pj) { 676

105 if (isOverlaped(pi->pActivity, pj->pActivity)) { 677

106 printActivity(pj->pActivity, output); 678

107 pi->next = pj->next; 679

108 free(pj->pActivity); 680

109 free(pj); 681

110 pj = pi->next; 682

111 } else { 683

112 pj = pj->next; 684

113 } 685

114 } 686

115 pi = pi->next; 687

116 } 688

117 689

118 return aList; 690

119 } 691

22

Page 24: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

120 692

121 int isOverlaped(Activity* a1, Activity* a2) { 693

122 return ( isTimeBeforeOrEqual(a1->startTime, a2->startTime) 694

123 && isTimeBefore(a2->startTime, a1->endTime) ) 695

124 || ( isTimeBeforeOrEqual(a2->startTime, a1->startTime) 696

125 && isTimeBefore(a1->startTime, a2->endTime) ); 697

126 } 698

127 699

128 int isTimeBeforeOrEqual(Time t1, Time t2) { 700

129 return (t1.hours < t2.hours) || 701

130 (t1.hours == t2.hours && t1.minutes <= t2.minutes); 702

131 } 703

132 704

133 int isTimeBefore(Time t1, Time t2) { 705

134 return (t1.hours < t2.hours) || 706

135 (t1.hours == t2.hours && t1.minutes < t2.minutes); 707

136 } 708

137 709

138 int isActivityBeforeOrEqual(Activity* a1, Activity* a2) { 710

139 return isTimeBeforeOrEqual(a1->startTime, a2->startTime); 711

140 } 712

141 713

142 void printActivity(Activity* actv, FILE* f) { 714

143 fprintf(f, "%d:%d-%d:%d ", actv->startTime.hours, actv->startTime.minutes, 715

144 actv->endTime.hours, actv->endTime.minutes); 716

145 fputs(actv->description, f); 717

146 } 718

147 719

148 void printActivityList(ListElement* aList, FILE* f) { 720

149 ListElement* p = aList; 721

150 while (p) { 722

151 printActivity(p->pActivity, f); 723

152 p = p->next; 724

153 } 725

154 } 726

155 727

156 void dealocate(ListElement* actv){ 728

157 ListElement* tmp; 729

158 while(actv){ 730

159 tmp = actv; 731

160 actv = actv->next; 732

161 /* Obavezno osloboditi i memoriju koju zauzima dinamicki alocirana 733

162 struktura koja predstavlja aktivnost! */ 734

163 free(tmp->pActivity); 735

164 free(tmp); 736

165 } 737

166 } 738739

23

Page 25: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

1.2.7 Drugo rešenje 740

U ovom odeljku, biće prikazano kompletno rešenje, koje prevazilazi okvire zahteva koji se 741

postavljaju za rešenja zadataka na ispitima, ali može poslužiti kao dobra referenca, u kojoj su 742

prikazane dobre programerske prakse koje bi trebalo koristiti pri svakom ozbiljnijem rešavanju 743

problema. 744

U listingu 1.26 prikazan je fajl zaglavlja (header file), u kome su prototipovi funkcija, sim- 745

boličke konstante i pomoćni makroi. Prikazani kôd sadrži komentare pojedinih elemenata, tako 746

da u tekstu neće biti diskutovani. Preporuka je da header fajlovi sadrže dovoljnu količinu ko- 747

mentara koji opisuju namene makroa, simboličkih konstanti, kao i uloge funkcija koje postoje, 748

i time pruže dokumentaciju samom programeru. 749

U listingu 1.28 prikazano je još jedno rešenje ovog zadatka. Najveće razlike vide se u funkciji 750

readActivity, koja obezbeđuje da se učitavanje izvrši do kraja linije, koristeći mehanizam 751

koji je malo drugačiji od onog opisanog ranije, prikazanog u listingu 1.15, ali se baziraju na 752

istoj ideji. 753

24

Page 26: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.26: Drugo rešenje - zaglavlje754

1 #ifndef _RASPORED_H_ 755

2 #define _RASPORED_H_ 756

3 757

4 // Default length of short text. 758

5 #define DEFAULT_TEXT_LEN 80 759

6 760

7 #define RESULT_OK 100 761

8 #define RESULT_FAILED -1 762

9 763

10 // Memory management functions/macros 764

11 // Used to make the source code independent of a concrete implementation. 765

12 #define MEM_ALLOC(Type) (Type*) malloc (sizeof(Type)) 766

13 #define MEM_FREE(ptr) free(ptr) 767

14 768

15 /* 769

16 * USER-DEFINED TYPES 770

17 */ 771

18 772

19 // Abstract type Time. 773

20 typedef struct sTime { 774

21 int hours; 775

22 int minutes; 776

23 } Time; 777

24 778

25 // Abstract type/concept Activity. 779

26 typedef struct sActivity { 780

27 Time startTime; 781

28 Time endTime; 782

29 char description[DEFAULT_TEXT_LEN+1]; 783

30 } Activity; 784

31 785

32 // Linked list element/node. 786

33 typedef struct sListElement { 787

34 Activity *pActivity; 788

35 struct sListElement *next; 789

36 } ListElement; 790

37 791

38 // Linked list 792

39 typedef struct { 793

40 ListElement *head; 794

41 ListElement *tail; 795

42 } List; 796

43 797

44 798

45 /* 799

46 * UTILITY FUNCTIONS FOR USER-DEFINED TYPES 800

47 */ 801

48 802

49 // Checks whether t1 is before t2. 803

50 // Returns 1 if true. Otherwise, 0 is returned. 804

51 int isTimeBefore(Time t1, Time t2); 805

52 806

53 // Checks whether time t1 is less or equal than t2. 807

54 // Returns 1 if true. Otherwise, 0 is returned. 808

55 int isTimeBeforeOrEqual(Time t1, Time t2); 809

56 810

57 // Creates an empty list. 811

58 List* createList(); 812

59 813

25

Page 27: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

60 // Deallocates list. 814

61 // Returns RESULT_OK on success. Otherwise, RESULT_FAILED is returned. 815

62 int freeList (List *list); 816

63 817

64 818

65 /* 819

66 * PROBLEM DOMAIN FUNCTIONS 820

67 */ 821

68 822

69 // Checks whether activity a1 starts before or at the same time as activity a2. 823

70 // Returns 1 if true. Otherwise, 0 is returned. 824

71 int isActivityBeforeOrEqual(Activity *a1, Activity *a2); 825

72 826

73 // Prints activity actv to file f. 827

74 void printActivity(Activity *actv, FILE *f); 828

75 829

76 // Insertes activity actv to list. 830

77 // Returns RESULT_OK on success. Otherwise, RESULT_FAILED is returned. 831

78 int insertActivity(List *list, Activity *actv); 832

79 833

80 // Read an activity from the file f. 834

81 // On success, the function returns a pointer to a completly loaded activity object 835

. 836

82 // Otherwise, a NULL pointer is returned. 837

83 Activity* readActivity(FILE *f); 838

84 839

85 // Checks whether activities a1 and a2 are overlapped. 840

86 // Returns 1 if the activities are overlapped. Otherwise, 0 is returned. 841

87 int isOverlapped(Activity *a1, Activity *a2); 842

88 843

89 // Removes overlapped activities from a chronologically ordered list of activities. 844

90 // Returns RESULT_OK on success. Otherwise, RESULT_FAILED is returned. 845

91 int removeOverlappedActivities(List *list, FILE *output); 846

92 847

93 #endif 848849

26

Page 28: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.27: Drugo rešenje850

1 #include <stdio.h> 851

2 #include <stdlib.h> 852

3 #include <string.h> 853

4 #include "raspored.h" 854

5 855

6 /* Time utility functions. */ 856

7 int isTimeBefore(Time t1, Time t2) { 857

8 return (t1.hours<t2.hours) || 858

9 (t1.hours==t2.hours&& t1.minutes<t2.minutes); 859

10 } 860

11 861

12 int isTimeBeforeOrEqual(Time t1, Time t2) { 862

13 return (t1.hours<t2.hours) || 863

14 (t1.hours==t2.hours && t1.minutes<=t2.minutes); 864

15 } 865

16 866

17 int isActivityBeforeOrEqual(Activity *a1, Activity *a2) { 867

18 return isTimeBeforeOrEqual(a1->startTime, a2->startTime); 868

19 } 869

20 870

21 Activity* readActivity(FILE *f) { 871

22 int result, lastChar; 872

23 Activity* actv = MEM_ALLOC(Activity); 873

24 if (actv == NULL) 874

25 return NULL; 875

26 876

27 result = fscanf(f, "%d:%d-%d:%d ", 877

28 &actv->startTime.hours, &actv->startTime.minutes, 878

29 &actv->endTime.hours, &actv->endTime.minutes); 879

30 880

31 if (result == EOF) { 881

32 free(actv); 882

33 return NULL; 883

34 } 884

35 885

36 // Set ’\0’ at position DEFAULT_TEXT_LEN-1 for determining incomplete line of 886

text. 887

37 // If the line is longer than DEFAULT_TEXT_LEN, the indicator will be 888

overwritten 889

38 // with a character value from the input. 890

39 actv->description[DEFAULT_TEXT_LEN - 1] = ’\0’; 891

40 892

41 fgets(actv->description, DEFAULT_TEXT_LEN, f); 893

42 894

43 // Reliability feature: 895

44 // If a description text in the file is longer than DEFAULT_TEXT_LEN, 896

45 // skip the rest of the line, and move the cursor to the next line. 897

46 lastChar = actv->description[DEFAULT_TEXT_LEN - 1]; 898

47 if (lastChar != ’\0’ && lastChar != ’\n’) { 899

48 char tmp[DEFAULT_TEXT_LEN +1], *result = NULL; 900

49 do { 901

50 tmp[DEFAULT_TEXT_LEN - 1] = ’\0’; 902

51 result = fgets (tmp, DEFAULT_TEXT_LEN, f); 903

52 } while (result != NULL 904

53 && tmp[DEFAULT_TEXT_LEN - 1] != ’\0’ 905

54 && tmp[DEFAULT_TEXT_LEN - 1] != ’\n’); 906

55 } 907

56 return actv; 908

57 } 909

27

Page 29: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

58 910

59 List* createList() { 911

60 List *list = MEM_ALLOC(List); 912

61 list->head = list->tail = NULL; 913

62 return list; 914

63 } 915

64 916

65 int insertActivity(List *list, Activity *actv) { 917

66 ListElement* curr = list->head, *prev = NULL; 918

67 ListElement *newElement = MEM_ALLOC(ListElement); 919

68 if (newElement == NULL) { 920

69 printf ("Error in function insertActivity: could not allocated list element 921

.\n"); 922

70 return RESULT_FAILED; 923

71 } 924

72 925

73 newElement->pActivity = actv; 926

74 while (curr != NULL) { 927

75 if (!isActivityBeforeOrEqual(curr->pActivity, actv)) 928

76 break; 929

77 prev=curr, curr=curr->next; 930

78 } 931

79 932

80 newElement->next = curr; 933

81 if (prev == NULL) { 934

82 list->head = newElement; 935

83 list->tail = newElement; 936

84 } else 937

85 prev->next = newElement; 938

86 939

87 return RESULT_OK; 940

88 } 941

89 942

90 void printActivity(Activity *actv, FILE *f) { 943

91 fprintf(f, "%02d:%02d-%02d:%02d ", 944

92 actv->startTime.hours, actv->startTime.minutes, 945

93 actv->endTime.hours, actv->endTime.minutes); 946

94 fputs(actv->description, f); 947

95 fputc(’\n’, f); 948

96 } 949

97 950

98 int freeList (List *list) { 951

99 while (list->head != NULL) { 952

100 ListElement *curr = list->head; 953

101 list->head = list->head->next; 954

102 if (curr->pActivity != NULL) 955

103 MEM_FREE(curr->pActivity); 956

104 MEM_FREE(curr); 957

105 } 958

106 return RESULT_OK; 959

107 } 960

108 961

109 int printList (List *list, FILE *outputFile) { 962

110 ListElement *p = list->head; 963

111 while (p != NULL) { 964

112 printActivity(p->pActivity, outputFile); 965

113 p = p->next; 966

114 } 967

115 return RESULT_OK; 968

116 } 969

28

Page 30: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

117 970

118 int isOverlapped(Activity *a1, Activity *a2) { 971

119 return 972

120 (isTimeBeforeOrEqual(a1->startTime,a2->startTime) && 973

121 isTimeBefore(a2->startTime, a1->endTime)) 974

122 || 975

123 (isTimeBeforeOrEqual(a2->startTime,a1->startTime) && 976

124 isTimeBefore(a1->startTime, a2->endTime)); 977

125 } 978

126 979

127 int removeOverlappedActivities(List *list, FILE *output) { 980

128 ListElement *pi=list->head, *pj=NULL; 981

129 while (pi != NULL) { 982

130 pj = pi->next; 983

131 while (pj != NULL) { 984

132 if (isOverlapped(pi->pActivity, pj->pActivity)) { 985

133 pi->next = pj->next; 986

134 printActivity(pj->pActivity, output); 987

135 if (pj->pActivity != NULL) 988

136 MEM_FREE(pj->pActivity); 989

137 MEM_FREE(pj); 990

138 pj = pi->next; 991

139 } else { 992

140 pj=pj->next; 993

141 } 994

142 } 995

143 pi = pi->next; 996

144 } 997

145 return RESULT_OK; 998

146 } 9991000

29

Page 31: Rešeniispitnizadaci - rti.etf.bg.ac.rsrti.etf.bg.ac.rs/rti/ir1p2/materijali/ispitni-zadaci-0.4.pdf · 1 Univerzitet u Beogradu 2 Elektrotehnički fakultet 3 Programiranje 2 4 OO1P2,

Listing 1.28: Drugo rešenje -funkcija main1001

1 int main () { 1002

2 List *activityList = createList(); 1003

3 Activity *currActivity; 1004

4 int status = EXIT_SUCCESS; 1005

5 1006

6 FILE *scheduleFile = fopen("kalendar.txt", "r"); 1007

7 FILE *overlapingFile = fopen("preklapanja.txt", "w"); 1008

8 FILE *debugFile = fopen("debug.txt", "w"); 1009

9 1010

10 if (scheduleFile == NULL) { 1011

11 printf("Cannot open input file."); 1012

12 status = EXIT_FAILURE; 1013

13 goto ON_ERROR; 1014

14 } 1015

15 if (overlapingFile == NULL) { 1016

16 printf("Cannot open output file."); 1017

17 status = EXIT_FAILURE; 1018

18 goto ON_ERROR; 1019

19 } 1020

20 if (debugFile == NULL) { 1021

21 printf("Cannot open debug file."); 1022

22 status = EXIT_FAILURE; 1023

23 goto ON_ERROR; 1024

24 } 1025

25 1026

26 while ( (currActivity = readActivity(scheduleFile)) != NULL ) { 1027

27 if (insertActivity(activityList, currActivity) != RESULT_OK) { 1028

28 printf("Could not insert activity into the list of activities.\n"); 1029

29 goto ON_ERROR; 1030

30 } 1031

31 } 1032

32 1033

33 printList(activityList, debugFile); 1034

34 if (removeOverlappedActivities(activityList, overlapingFile) != RESULT_OK) { 1035

35 printf("Could not remove overlapped activities from the list of activities 1036

.\n"); 1037

36 goto ON_ERROR; 1038

37 } 1039

38 printList(activityList, debugFile); 1040

39 1041

40 ON_ERROR: // Resource deallocation. 1042

41 if (scheduleFile != NULL) fclose(scheduleFile); 1043

42 if (overlapingFile != NULL) fclose(overlapingFile); 1044

43 if (debugFile != NULL) fclose(debugFile); 1045

44 freeList(activityList); 1046

45 1047

46 return status; 1048

47 } 10491050

30