Fundamentele programării (IV) - Swift

În cadrul acestui episod vom vedea cum putem face în așa fel încât programele să ia anumite decizii în funcție de circumstanțe.

Până acum am scris doar programe sau scurte secvențe de cod în care instrucțiunile se executau în ordine, începând cu prima și terminând cu ultima. Nimic nu putea afecta acest proces. Dar, programele trebuie să poată face mai mult decât atât...

Să ne imaginăm că ajungem la o răscruce de drumuri; cel din dreapta duce spre Împărăția Roșie, iar celălalt spre Împărăția Verde. În funcție de unde vrem să ajungem, vom alege drumul din dreapta sau cel din stânga.

Pentru a o lua la dreapta, trebuie ca o anumită condiție să fie îndeplinită; trebuie să vrem să mergem în Împărăția Roșie. Pentru a o lua la stânga, trebuie ca acea condiția să nu fie îndeplinită; trebuie să nu vrem să mergem în Împărăția Roșie, ceea ce înseamnă că vrem să mergem în Împărăția Verde.

Condiția poate lua diverse forme. În exemplul anterior condiția a fost voința noastră; dar, ar putea să fie ceva mai interesant; de exemplu, am putea spune că dacă în caleașca pe care o însoțim se află o prințesă, atunci vom merge în Împărăția Roșie.

Instrucțiunea if

Pentru a explica ce vrem să facem, am folosit cuvântul dacă; am spus că dacă o anumită condiție este îndeplinită vom efectua o anumită acțiune. Și în Swift vom folosi ceva similar; va fi în engleză, deci cuvântul va fi if. El va fi urmat de condiția care trebuie verificată  și apoi de un așa numit bloc de instrucțiuni; acestea trebuie executate în cazul în care condiția este îndeplinită. Condiția este o expresie booleană; poate fi o simplă variabilă sau o expresie mai complexă. O astfel de instrucțiune este numită condițională.

Un astfel de bloc este format dintr-o succesiune de instrucțiuni, cuprinse între acolade.

Să presupunem că avem o variabilă existaPrintesa care are o anumită valoare; nu știm exact cum a fost atribuită acea valoare și nici care este valoarea sa curentă. Dar, dacă în acest moment această variabilă are valoarea true, dorim să scriem mesajul "Împărăția Roșie". Secvența de cod ar arăta astfel:

Secvența este corectă, dar nu arată prea bine. De obicei instrucțiunea care ar trebui executată atunci când este îndeplinită condiția este scrisă pe o linie nouă.

Altfel de condiții

Spuneam că acea condiție este o expresie booleană. Până acum am folosit o simplă variabilă booleană; este ușor să ne gândim că putem folosi operatorii booleeni pe care i-am prezentat în episodul trecut. Poate dacă în caleașcă se află și un prinț, dorim de fapt să mergem la nunta din Împărăția Verde. Codul ar putea arăta așa:

Dar, de obicei condițiile pe care dorim să le verificăm sunt cu totul altele. Dacă lucrăm cu numere, am putea dori să verificăm dacă un număr are o anumită valoare, să vedem dacă un număr este mai mare decât altul etc.

Să considerăm un prim exemplu: avem o variabilă nr care conține un număr întreg; în cazul în care valoarea acelei variabile este 2457, dorim să scriem mesajul "Număr magic". Codul este:

Observăm că nu am scris un singur simbol =, ci două; == este operatorul de egalitate. Folosim un singur = pentru atribuire. Secvența următoare nu este validă:

Operatorul de egalitate este aplicat asupra a doi operatori cu tipuri compatibile; pentru tipurile primitive nu avem nicio surpriză; rezultatul este o valoare booleană care arată dacă cele două valori sunt egale.

Avem și un operator de inegalitate; rezultatul arată dacă cele două valori nu sunt egale.

După cum vedeți, operatorul este !=. Intuitiv, am putea spune că am negat egalitatea; am folosit semnul exclamării (cel care indică negația) urmat de semnul egal.

Dar, numerele pot fi și comparate; nu ar trebui să ne surprindă deloc existența unor operatori ca < sau >. Să verificăm dacă un număr este strict pozitiv:

La fel de simplu este să verificăm dacă numărul este strict negativ:

Când comparăm numerele folosim câteodată și conceptul de mai mare sau egal. Avem și un operator pentru așa ceva; nu este chiar  cum poate ne-ar plăcea, ci >=. Să vedem cum verificăm dacă un număr este pozitiv (nu strict pozitiv):

Similar, avem operatorul <= pentru mai mic sau egal.

Condiții compuse

Rezultatul utilizării oricărui astfel de operator este o valoare booleană; este natural să ne așteptăm să putem folosi operatorii booleeni asupra rezultatelor.

Am putea dori să verificăm dacă un anumit număr întreg este format dintr-o singură cifră. Am avea o condiția dublă: numărul trebuie să fie mai mare sau egal cu zero și mai mic decât zece. Am putea scrie următorul cod:

Mai avem și alte variante; una ar putea fi:

Codul funcționează corect fiindcă operațiile de comparare sunt executate înaintea conjucției logice; există o ordine specifică limbajului și de data aceasta ordinea de convine; dar, ar fi cam mult să ținem minte ordinea luată în considerare de limbaj; de obicei vrem să ne asigurăm că operațiile sunt efectuate în ordinea pe care o dorim; pentru aceasta folosim paranteze, chiar dacă nu sunt neapărat necesare; codul poate fi înțeles mult mai ușor. Așadar, varianta recomandată ar fi:

Putem avea și paranteze în paranteze, dar totul este în regulă; totuși, să nu vă gândiți că am putea folosi paranteze drepte sau acolade; am întâlnit deja acolade și am văzut că au cu totul alt rol (delimitează grupuri de instrucțiuni); vom întâlni și paranteze drepte, care au și ele un rol diferit.

Deși nu prea are sens să scriem un astfel de cod, pentru a ilustra că nu tot timpul ordinea este cea pe care am dori-o să verificăm dacă un număr este diferit del 2457 verificând egalitatea și negând rezultatul. Codul ar arăta așa:

Vom observa că este generată o eroare care ne spune că operatorul logic de negație nu poate fi aplicat asupra unui număr întreg. Totul se rezolvă dacă scriem:

Să ne amintim că în Swift operatorul de negație trebuie să fie lipit de expresie; următoarea variantă nu este corectă:  

Dacă avem && și prima dintre cele două condiții este falsă, atunci a doua nu mai este verificată fiindcă rezultatul ar fi false oricum. Similar, dacă avem || și prima dintre cele două condiții este adevărată, atunci a doua nu mai este verificată fiindcă rezultatul ar fi true oricum. Deocamdată nu pare foarte important; dar, dacă ne gândim că aceste condiții pot deveni din ce în ce mai complicate, ne putem imagina că am putea ajunge să economisim timp dacă nu facem verificări inutile. Să revenim la codul care verifică dacă un număr are o singură cifră. Dacă numărul ar fi negativ, prima condiție este falsă și nu mai are rost să verificăm dacă el este mai mic decât zece. Chiar dacă ar fi (și în cazul nostru știm că va fi fiindcă este negativ), nu are nicio relevanță; condiția compusă va fi falsă.

Alternativa

Să revenim la exemplul inițial; spuneam că dacă avem o prințesă în caleașcă vrem să mergem în Împărăția Roșie. Probabil că dacă nu însoțim nicio prințesă am vrea totuși să mergem în Împărăția Verde (poate găsim o prințesă acolo?). Pentru a verifica dacă trebuie să mergem în Împărăția Verde, am putea nega condiția; codul ar arăta astfel:

Deocamdată nu avem nicio problemă, dar pare complicat și ne putem imagina că în situații complexe ar deveni din ce în ce mai ciudat. Codul pe care l-am scris ar putea fi descris astfel: dacă în caleașcă este o prințesă mergem în Împărăția Roșie; dacă în caleașcă nu este o prințesă mergem în Împărăția Verde. Pare puțin ciudat și cam lung; ne-am aștepta ca raționamentul să fie ceva de genul: dacă în caleașcă este o prințesă mergem în Împărăția Roșie; altfel mergem în Împărăția Verde. Din fericire, Swift și majoritatea limbajelor de programare ne permit să "spunem" altfel. Cuvântul va fi în engleză: else.

În Swift vom avea cuvântul if, urmat de condiția care trebuie verificată, de blocul de instrucțiuni care trebuie executate în cazul în care condiția este îndeplinită, apoi de cuvântul else și de instrucțiunea sau blocul de instrucțiuni care trebuie executate în cazul în care condiția nu este îndeplinită.

Codul nostru devine:

De obicei e bine să folosim blocuri de instrucțiuni formate dintr-o singură instrucțiune pentru a avea cod mai ușor de citit.

If în if

Am spus că în cazul în care este îndeplinită o condiție se execută un bloc de instrucțiuni. Am mai arătat că dacă nu este îndeplinită condiția, putem specifica un alt bloc de instrucțiuni care să fie executate. Nu am pus nicio restricție legată de tipul instrucțiunilor respective. De fapt, nu există nicio restricție; putem pune chiar și o instrucțiuni condiționale.

Așadar, putem verifica o condiție și apoi, printre operațiile pe care le efectuăm, putem verifica o altă condiție și efectua alte operații în funcție de ea.

Să extindem puțin exemplul inițial; la răscrucea de drumuri putem alege trei variante: spre Împărăția Roșie, spre Împărăția Verde sau spre Împărăția Albastră. În caleașcă putem avea sau nu o prințesă și putem sau nu avea un prinț. Dacă prințesa nu este în caleașcă, atunci mergem în Împărăția Albastră să aducem prințesa; dacă e în caleașcă și nu e însoțită, mergem în Împărăția Verde să luăm prințul; dacă e însoțită de prinț, mergem la nuntă în Împărăția Roșie.

Așadar, vom avea o condiția care verifică dacă prințesa există. Dacă da, atunci trebuie să verificăm dacă există și prințul. Dacă prințul există, scriem mesajul "Împărăția Roșie". Dacă prințul nu există, scriem mesajul "Împărăția Verde". Dacă prințesa nu există, scriem mesajul "Împărăția Albastră" indiferent dacă prințul există sau nu.

Codul ar putea arăta așa:

Nu trebuie neapărat să ne oprim la un singur if în if. Cel din interior poate conține la rândul său o instrucțiune condițională; la fel acest al treilea if și așa mai departe.

Când avem două sau mai multe astfel de instrucțiuni if, spunem că ele sunt imbricate.

Va urma

În următorul episod vom vedea ce opțiuni avem la dispoziție în cazul în care o anumită variabilă (sau expresie) poate avea diferite valori și trebuie să executăm diverse instrucțiuni în funcție de fiecare valoare în parte.