Fundamentele programării (III) - Scala

Cel de-al treilea episod al serialului dedicat fundamentelor programării va prezenta detalii legate de modul în care sunt executate diverse operații. De asemenea, vom vedea cum putrem adăuga informații suplimentare care nu afectează deloc modul în care sunt executate programele, dar ajută la înțelegerea acestora.

Altfel de atribuiri

În cadrul episodului anterior am văzut cum putem modifica valoarea unei variabile cu ajutorul instrucțiunii de atribuire. Dar, toate atribuirile pe care le-am prezentat nu făceau decât să dea unei variabile o simplă valoare.

Limbajele ne permit mult mai mult. De exemplu, am putea să atribuim unei variabile valoarea altei variabile. Să considerăm următoarea secvență:

În urma executării acestor instrucțiuni, atât variabila a, cât și variabila b, vor avea valoarea 3. Instrucțiunea din linia 4 indică faptul că variabilei b trebuie să i se atribuie valoarea variabilei a. Cele două variabile sunt în continuare independente. Dacă am continua programul cu a = 5 doar valoarea variabilei a se va modifica; valoarea variabilei b va rămâne 3.

Să facem acum un mic exercițiu. Să presupunem că avem două variabile a și b care conțin două valori diferite (pentru exemplul nostru vom considera că acestea sunt 2 și 4). Dorim să scriem un program care schimbă între ele cele două valori. Cele două variabile vor fi declarate și inițializate astfel:

Evident, pentru a rezolva problema, am putea scrie:

Dar, să ne închipuim că dorim ca programul să funcționeze corect indiferent care sunt valorile inițiale (de exemplu, am putea presupune că ele sunt introduse de utilizator și în momentul scrierii programului nu știm care vor fi.

Având în vedere că tocmai am prezentat cum unei variabile i se poate stribui valoarea altei variabile, ne-am putea gândi la o soluție de genul:

Dar, să vedem ceva ce se întâmplă; inițial a are valoare 2 și b are valoarea 4. Prin instrucțiunea a = b variabilei a i se stribuie valoarea curentă a variabilei b, adică 4. Așa cum am spus în cadrul episodului anterior, valoarea 2 se pierde. Cea de-a doua instrucțiune face ca variabilei b să i se atribuie valoarea variabilei a; dar această valoare este acum 4, deci în urma executării instrucțiunii, valoarea variabilei b va fi tot 4. Astfel, ajungem să avem aceeași valoare pentru ambele variabile și nu am schimbat valorile între ele.

Am spus că variabilele pot fi privite ca fiind niște recipiente. Să ne imaginem că cele două variabile ar fi două pahare pline cu lichide diferite și dorim să schimbăm lichidele între ele. Pentru a putea muta lichidul dintr-un pahar în altul, va trebui să vărsăm lichidul dintr-un pahar pentru a face loc. În principiu, lichidul se pierde în acest mod. Dar, l-am putea vărsa într-un al treilea pahar, nu?

Atunci, pentru a schimba lichidele între ele, vom lua un al treilea pahar, vom turna în el lichidul din primul pahar, apoi vom turna în primul pahar lichidul din al doilea și, la final, vom turna în al doilea pahar lichidul din cel de-al treilea. Astfel, în primul pahar vom avea lichidul care a fost inițial în al doilea, iar în al doilea pahar vom avea lichidul care a fost inițial în primul.

Ne dăm seama destul de ușor că pentru a schimba valorile a două variabile, avem nevoie de o a treia variabilă (ajutătoare). Secvența care schimbă valorile ar putea fi:

Am declarat variabila c și am și inițializat-o. După cum se vede și la inițializare e la fel ca la atribuire, Putem inițializa o variabilă cu valoarea altei variabile.

Prima operație

Totuși, până acum doar am declarat variabile, le-am dat valori și ne-am jucat puțin cu ele. Dar, lucrăm cu un calculator; la un moment dat ar trebui să facem și niște calcule, nu? Deși pare că e cam târziu fiindcă suntem deja la al treilea episod, vom realiza acum prima adunare. Presupunem că două variabile au anumite valori pe care dorim să le adunăm și să păstrăm rezultatul într-o a treia variabilă. Nu e nicio mare filosofie, codul arată cam cum v-ați aștepta;

Nu e nicio surpriză, valoarea variabilei c va fi 6. Dar, vedem că am atribuit variabilei c altceva, nu o simplă valoare și nici valoarea unei alte variabile. Am cerut efectuarea unui calcul, a unei operații; descrierea acestei operații poartă denumirea de expresie. De fapt, și anterior am folosit tot expresii, dar foarte simple. Valoarea simplă este și ea o expresie, la fel și valoarea unei variabile.

Așa cum putem atribui unei variabile o expresie, la fel putem și inițializa o variabilă cu o expresie. Codul de mai jos are același efect ca cel anterior:

Expresii

Expresille pot fi foarte complexe. Ele pot conține valori simple, valori ale unor variabile, operatori și multe altele. De exemplu, putem aduna trei numere, dintre care doar două sunt păstrate în variabile, iar al treilea este o valoare simplă:

Așa cum vă așteptați, putem realiza și alte operații matematice. Pentru scăderi și înmulțiri nu avem nicio surpriza. La împărțire lucrurile sunt mai complicate și vom reveni.

Pentru înmulțire, operatorul folosit este *; putem efectua o înmulțire astfel:

Putem combina operațiile și regulile matematice se aplică:

Valoarea variabilei c va fi 11. Dacă dorim să modificăm ordinea firească a operațiilor, putem folosi paranteze:

În acest caz valoarea variabilei c va fi 20. Putem avea și paranteze în paranteze, dar nu chiar ca la matematică. Nu folosim paranteze drepte sau acolade. Toate parantezele sunt rotunde:

De data aceasta c va avea valoarea 12. Prima dată se calculează valoarea a - 2; rezultatul este 0. Urmează înmulțirea valorii variabilei a cu acest rezultat fiindcă înmulțirile se efectuează înaintea adunărilor dacă nu sunt paranteze; noul rezultat este tot 0. Urmează adunarea între 3 și acest rezultat (obținem 3), iar apoi înmulțirea cu b care duce la rezultatul final: 12.

Împărțirea

Dacă împărțim numărul 3 la 2 spunem fie că rezultatul este 1.5, fie că obținem câtul 1 și restul 1. În Scala, dacă împărțim două numere întregi, rezultatul operației este câtul, iar operatorul folosit este /. Dacă unul dintre numere este cu zecimale, atunci rezultatul este și el cu zecimale, nu doar câtul. Să vedem un exemplu:

Valoarea variabilei c va fi 1. Dar, dacă avem:

valoarea variabilei c va fi 1.5. Rezultatul va fi cu zecimale, dacă cel puțin unul dintre cele două numere este cu zecimale. De exemplu, și în următorul caz rezultatul este tot 1.5:

La fel stau lucrurile în situația următoare:

Însă, dacă cele două numere împărțite sunt întregi, rezultatul este 1, chiar dacă variabila căreia i-l atribuim reprezintă un număr cu zecimale:

Putem face experimente mai interesante. Nu trebuie să folosim neapărat variabile; valorile simple sunt suficiente. Încercați să vă dați seama ce valori vor avea variabilele declarate astfel:

În Scala, dacă scriem 2, numărul este întreg, dar dacă scriem 2.0 el este considerat cu zecimale (partea fracționară fiind 0). Pe baza acestei informații ne dăm seama destul de ușor că valoarea variabilei a va fi 1, iar celelalte trei vor avea valoarea 1.5.

Spuneam că dacă împărțim două numere întregi, rezultatul va fi câtul operației matematice. Ni se oferă și posibilitatea de a afla restul. Pentru aceasta vom folosi un alt operator, anume %. În situația:

valoarea variabilei c va fi 2.

Deși e oarecum bizar, operatorul % poate fi folosit și pentru numere cu zecimale. Rezultatul este tot un fel de rest; se calculează partea întreagă a împărțirii și rezultatul este dat de "ce rămâne". Să vedem un exemplu:

Valoarea variabilei c va fi 0.5. Partea întreagă a împărțirii este 3; pentru acest rezultat se "consumă" 7.5 din 8 și "rămâne" 0.5.

Am văzut că putem transforma un număr întreg în unul cu zecimale adăugând .0. Avem și alternative: putem adăuga f, F, d sau D. Spuneam că numerele sunt numere simple dacă nu apare nimic legat de ele. Dacă adăugăm una dintre aceste litere schimbăm semnificația. Practic, transformăm un număr întreg într-unul cu zecimale. În Scala avem două tipuri pentru astfel de numere: Float și Double. Dacă adăugăm f sau F, numărul este considerat a fi de tip Float; dacă adăugăm d sau D, el este considerat a fi de tip Double. Deocamdată nu are relevanță dacă avem Float sau Double...

Operații logice

Putem efectua operații și cu variabilele booleene. Chiar dacă au doar două valori posibile, există operații care pot fi efectuate. În primul rând avem negația; este o operație prin care adevărul devine fals și falsul devine adevăr. Operatorul este !; să îl folosim în cele două posibile situații:

Valoarea variabilei c va fi false, iar cea a variabilei d va fi true.

Dar, avem și operații puțin mai complicate.

De exemplu, putem efectua o operație care să ne spună dacă două valori booleene sunt ambele adevărate. Operația se numește ȘI; mai riguros, ea este numită conjuncție. Să vedem un exemplu:

Probabil v-ați dat seama, operatorul este &. Valoarea variabilei d va fi true, iar cea a variabilei e va fi false.

O altă operație pe care o avem la dispoziție ne spune dacă cel puțin una dintre două valori booleene este adevărată. Operația se numește SAU; mai riguros, ea este numită disjuncție. De data aceasta operatorul este |. Să vedem acum câteva variante:

Doar g va avea valoarea false, fiindcă atât c, cât și d, au valoarea false. e și f vor avea valoarea true fiindcă cel puțin una dintre cele două variabile asupra căreia este efectuată operația are valoarea true (ambele pentru e, doar prima pentru f).

Acest SAU logic nu prea seamănă cu cel pe care îl folosim natural. De exemplu, dacă spunem că vrem o rochie roșie sau albastră, vrem să spunem că rochia poate fi fie roșie, fie albastră. Nu vrem să spunem că rochia poate fi și roșie și albastră; nu vrem nici să spunem că dorim două rochii, una roșie și una albastră (deși poate nu ar deranja pe nimeni). Un alt exemplu ar fi când spunem că dorim banane sau portocale; vrem una dintre cele două variante, nu amândouă.

Există o operație logică al cărei rezultat ne spune dacă exact una dintre cele două valori booleene este adevărată. Rezultatul este false dacă ambele valori sunt true sau dacă ambele valori sunt false. Acest sau natural este numit SAU EXCLUSIV sau disjuncție exclusivă. Operatorul este ^; să vedem exemplul:

Doar f va avea valoarea true, fiindcă a are valoarea true și c are valoarea falsee și g vor avea valoarea false fiindcă ambele variabile asupra căreia este efectuată operația au aceeași valoare (true pentru e, false pentru g).

Operații cu string-uri

Operatorul + poate fi utilizat și pentru string-uri. Aplicat asupra a două varioabile de tip String, operatorul duce la unirea acestora. De exemplu, rezultatul operației "alpha" + "beta" este "alphabeta". Operația poartă denumirea de concatenare. Ne putem reaminti de o instrucțiune din programul din primul episod:

Aici avem o concatenare de trei string-uri.

Rezultatul concatenării unor string-uri are tipul String. Dar, putem aplica operatorul + și în cazul în care doar una dintre cele două valori are tipul String, iar rezultatul este cel pe care îl așteptăm. Iată câteva exemple:

Valoarea variabilei e va fi ceva2, cea a variabilei f va fi 3.0ceva, iar cea a variabilei g va fi cevafalse. 3.0ceva pare cam ciudat; nu e 3ceva fiindcă numerele cu zecimale apar cu zecimale chiar dacă partea fracționară lipsește.

Atribuiri mai interesante

Ce-ați zice dacă o variabilă ar apărea de ambele părți ale semnului = ale unei instrucțiuni de atribuire. Următoarea secvență este validă, dar nu prea interesantă fiindcă nu se întâmplă nimic special:

Totuși, am putea avea o expresie în dreapta. De exemplu:

Dacă a = a + 1 ar fi fost o ecuație, așa ceva nu ar avea sens. Dar, instrucțiunea de mai sus se traduce astfel: preia valoarea variabilei a, adună 1 și pune rezultatul în variabila a. Inițial a are valoarea 1, adunăm 1 și obținem 2 și a primește valoarea 2. Faptul că acest lucru este posibil este foarte util. Vă mai amintiți exemplul cu variabila goluriGazde care conține numărul golurilor înscrise de echipa gazdă într-un meci de fotbal. În momentul în care echipa gazdă marchează un gol, valoarea acestei variabile trebuie să devină mai mare cu 1. Am putea scrie:

De fapt, astfel de instrucțiuni sunt atât de des folosite, încât ni se oferă posibilitatea să o scriem și prescurtat. Următoarea instrucțiune este echivalentă:

Mai mult, atât de des este nevoie ca valoarea unei variabile să fie mărită cu exact 1, încât avem la dispoziție o prescurtare suplimentară:

Există și varianta ++goluriGazde, care are același efect. Pentru a explica diferența sunt necesare cunoștințe mai avansate; deocamdată vom considera că cele două variante sunt echivalente. Operația prin care valoarea unei variabile crește cu 1 este numită incrementare.

Pentru majoritatea operatorilor avem variante prescurtate: există -=, *=, /=, %=, &=, |= și multe altele.

Avem o prescurtare suplimentară și pentru decrementare (valoarea unei variabile scade cu 1). Operatorul corespunzător este --; el poate apărea înainte sau după variabilă.

Despre spații

În cadrul diverselor secvențe de cod prezentate până acum ați văzut că, în cadrul instrucțiunilor, diverse elemente sunt separate prin spații. Aceste spații pot lipsi de cele mai multe ori și, în marea majoritate a cazurilor, putem pune oricâte spații.

Spațiul este obligatoriul doar dacă absența sa ar crea confuzii. De exemplu, există o mare diferență între var a = 2 și vara = 2. Prima variantă declară o variabilă a și o inițializează cu valoarea 2. Cea de-a doua atribuie variabilei vara valoarea 2. Dacă o astfel de variabilă nu a fost declarată anterior, atunci instrucțiunea nu este validă. Dar, de exemplu, putem elimina spațiile din jurul operatorului =; varianta var a=2 reprezintă o declarație cu inițializare validă. Putem pune și mai multe spații dacă dorim:

Nu arată foarte bine, dar este corect. De fapt, de cele mai multe ori modul în care utilizăm spațiile are rol estetic; ne ajută să vedem mai ușor codul.

Pentru a separa elementele limbajului, putem folosi și taburi sau putem trece pe o linie nouă. Nu prea putem ilustra aici taburile (ar arăta tot ca niște spații), dar putem ilustra folosirea mai multor linii:

Din nou, parcă nu am vrea să arate așa codul nostru, dar este permis. După cum vedeți, am folosit și multe linii și multe spații.

Simbolurile care nu se văd și sunt folosite pentru a separa diverse elemente poartă denumirea de caractere albe. Deocamdată am văzut că astfel de caractere sunt spațiile, taburile și marcajele de sfârșit de linie.

Trebuie precizat faptul că în interiorul string-urilor caracterele albe își păstrează semnificația. De exemplu, string-urile "a   b" și "a b" sunt diferite.

Comentarii

Uneori scriem cod pe care dorim să nu îl mai folosim deocamdată, dar poate vom avea nevoie în viitor. Putem marca acest cod astfel încât el să nu se execute. Porțiunile marcate astfel poartă denumirea de comentarii.

În Scala avem două tipuri de comentarii. Pe o linie, putem pune secvența // și tot ce urmează după ea este comentariu.

Putem folosi și comentarii pe mai multe linii. Vom marca începutul comentariului prin /* și sfârșitul său prin */.

Dacă // sau /* apar în interiorul unui string, atunci își pierd semnificația. De exemplu var s: String = "a//a"  este o instrucțiune care inițializează string-ul s cu valoarea a//a. La fel, nu avem un comentariu în instrucțiunea var s: String = "a/*a".

Dar, putem folosi comentariile nu numai pentru a elimina temporar cod, ci și pentru a-l explica. Când altcineva citește codul nostru, îi va fi mult mai ușor să înțeleagă dacă acesta conține explicații. Acel "altcineva" poate fi chiar persoana care a scris codul, o lună mai târziu. În multe cazuri ne va fi foarte greu să ne dăm seama ce și cum am vrut să facem.

Trebuie să fim puțin atenți în câteva situații. Să presupunem că avem următorul cod:

Să presupunem că nu mai avem nevoie de variabilele c și d. Am putea comenta liniile corespunzătoare:

Dacă dorim să arate mai frumos, am putea și așa:

Acum să presupunem că am dori să scăpăm și de variabilele b și e. Soluția care pare la îndemână ar fi:

Dar... nu am luat în considerare faptul că ce apare în interiorul comentariului este ignorat până în momentul în care este întâlnită secvența care marchează sfârșitul comentariului. Comentariul nostru începe pe linia 2 și secvența de sfârșit este întâlnită pe linia 7. Secvența de pe linia 9 este acum invalidă.

Un alt exemplu interesant ar putea fi următorul:

Am putea crede că avem un simplu comentariu care ne spune care sunt operatorii permiși într-o anumită situație. Dar, înșirurirea operatorilor conține secvența */ care indică sfârșitul comentariului. Ce rămâne nu mai este corect.

Va urma

În cadrul următorului episod vom vedea cum programele pot lua anumite decizii în funcție de împrejurări.