U lekciji "Logički tip i logičke operacije" imali smo zadatak sa papirom i kovertom. Ovde cemo rešiti nešto složeniji zadatak. Date su dimenzije tri pravougaona lista papira. Potrebno je odrediti veličinu najmanje koverte u koju sva tri lista mogu da stanu bez savijanja, tako da im ivice budu paralelne ivicama koverte.
Neka su dimenzije papira redom a1_X_b1, a2_X_b2 i a3_X_b3. Pretpostavimo za trenutak da je a1≥b1, a2≥b2 i a3≥b3. Ako sada najveći od brojeva a1, a2, a3 označimo sa najveci_a, a najveći od brojeva b1, b2, b3 sa najveci_b, tada su dimenzije tražene koverte upravo najveci_a x najveci_b.
Ispitivanje bi moglo prilično da se zakomplikuje ukoliko nejednakosti a1≥b1, a2≥b2 i a3≥b3 nisu ispunjene.
Kod ovakvih papira morali bismo da proveravamo više slučajeva sa različito okrenutim papirima, na primer kao što je prikazano ispod.
Ovaj problem bismo najlakše rešili ako postignemo da navedene nejednakosti postanu ispunjene. Da bismo obezbedili, na primer, da a1 ne bude manje od b1 možemo da izvršimo sledeće:
if (a1 < b1) { int pomocna = a1; a1 = b1; b1 = pomocna; }
Ovim naredbama postižemo da promenljive a1 i b1 razmene vrednosti ako je bilo a1<b1.
Posle ove if naredbe, promenljive a1 i b1 i dalje sadrže ista dva broja kao i pre (papir je i dalje istih dimenzija), ali sada sigurno važi a1≥b1 (papir je postavljen vodoravno, kao što će biti i koverta). Uradimo isto i za ostala dva papira i dobijamo program koji rešava zadatak.
static void Main(string[] args) { int a1, b1, a2, b2, a3, b3; int pomocna, najveci_a, najveci_b; Console.WriteLine("Unesite dimenzije prvog papira: "); a1 = int.Parse(Console.ReadLine()); b1 = int.Parse(Console.ReadLine()); if (a1 < b1) { pomocna = a1; a1 = b1; b1 = pomocna; } Console.WriteLine("Unesite dimenzije drugog papira: "); a2 = int.Parse(Console.ReadLine()); b2 = int.Parse(Console.ReadLine()); if (a2 < b2) { pomocna = a2; a2 = b2; b2 = pomocna; } Console.WriteLine("Unesite dimenzije treceg papira: "); a3 = int.Parse(Console.ReadLine()); b3 = int.Parse(Console.ReadLine()); if (a3 < b3) { pomocna = a3; a3 = b3; b3 = pomocna; } najveci_a = Math.Max(a1, Math.Max(a2, a3)); najveci_b = Math.Max(b1, Math.Max(b2, b3)); Console.WriteLine("Najmanja koverta u koju mogu da stanu sva tri papira: "); Console.WriteLine(najveci_a + " x " + najveci_b); }
Funkcija Math.Max kao rezultat vraća veći od dva broja koja joj prosledimo kao parametre, na primer Math.Max(5,3) daje rezultat 5. Kombinovanjem dva poziva funkcije Math.Max, kao u Math.Max(a1, Math.Max(a2, a3)), dobijamo najveći od tri broja.
Vidimo da se ovde deo programa koji uređuje dva broja po veličini pojavljuje tri puta. Ranije smo ovakva ponavljanja izbegavali uvođenjem funkcija. Međutim, ukoliko bismo ovde napisali sledeću funkciju, ona ne bi radila onako kako očekujemo.
static void Uredi(int a, int b) { int pomocna; if (a < b) { pomocna = a; a = b; b = pomocna; } }
Razlog leži u tome što, kada se parametri funkcije ovako zadaju, funkcija napravi svoje kutije za parametre, i u te kutije prepiše vrednosti iz naših kutija sa mesta poziva funkcije. Kada funkcija završi sa radom, ona svoje kutije baci, a u našim kutijama vrednosti ostaju kakve su na početku i bile. Rečeno jezikom programera, funkcija napravi svoje privatne kopije parametara i koristi te kopije pri izvršavanju. Po završetku, funkcija oslobađa prostor koji je zauzela za kopije i vrednosti kopija se pri tome gube, a originalne promenljive ostaju nepromenjene.
Potrebno je da dodamo reč ref ispred tipa i imena parametra ako hoćemo da funkcija radi sa originalnim promenljivama.
void Uredi(ref int a, ref int b) { int pomocna; if (a < b) { pomocna = a; a = b; b = pomocna; } }
Ova mala razlika omogućava da promene koje funkcija napravi na parametrima budu trajne, to jest da se dogode na originalnim promenljivama sa mesta poziva funkcije.
Pomenimo uzgred da za parametre bez reči ref još kažemo da se u funkciju prenose po vrednosti (engl. pass by value), jer se vrednost parametra kopira u novu kutiju (promenljivu) u okviru funkcije. Nasuprot tome, za parametre obeležene sa ref kažemo da se prenose po referenci (engl. pass by reference), jer se funkciji samo kaže gde je parametar, tj. daje joj se referenca na kutiju (promenljivu) koja sadrži parametar.
Kada parametre prenosimo po referenci, onda je potrebno to naglasiti i u pozivu funkcije unutar funkcije Main:
Uredi(ref a, ref b);
Program bi na kraju mogao da izgleda ovako:
class Program { static void Uredi(ref int a, ref int b) { int pomocna; if (a < b) { pomocna = a; a = b; b = pomocna; } } static void Main(string[] args) { int a1, b1, a2, b2, a3, b3; int najveci_a, najveci_b; Console.WriteLine("Unesite dimenzije prvog papira: "); a1 = int.Parse(Console.ReadLine()); b1 = int.Parse(Console.ReadLine()); Uredi(ref a1, ref b1); Console.WriteLine("Unesite dimenzije drugog papira: "); a2 = int.Parse(Console.ReadLine()); b2 = int.Parse(Console.ReadLine()); Uredi(ref a2, ref b2); Console.WriteLine("Unesite dimenzije treceg papira: "); a3 = int.Parse(Console.ReadLine()); b3 = int.Parse(Console.ReadLine()); Uredi(ref a3, ref b3); najveci_a = Math.Max(a1, Math.Max(a2, a3)); najveci_b = Math.Max(b1, Math.Max(b2, b3)); Console.WriteLine("Najmanja koverta u koju mogu da stanu sva tri papira: "); Console.WriteLine(najveci_a + " x " + najveci_b); } }