|
Este minunat sa poti lucra cu datele in forma lor elementara, dar toti stiu ca partea amuzanta se afla in cadrul colectiilor.
O colectie de date (denumita uneori container) reprezinta un obiect ce contine un grup de elemente inrudite; aceasta relatie poate fi dupa tip (de exemplu toate elementele continute sunt siruri de caractere), scop (de exemplu toate sunt nume) sau dupa sortimentul de branza favorit (al meu este provolone). O colectie poate fi utilizata pentru a gazdui un numar de articole, de a le pastra organizate si de a efectua operatiuni asupra membrilor; in acelasi timp, fiecare membru (sau element) al colectiei este un obiect separat, vizibil, asupra caruia se poate opera (de ex. poate apela metode, poate fi adunat sau se poate scadea etc.).
Intervalul
Primul si cel mai elementar tip de colectie este intervalul. Intervalele detin o secventa de valori cum ar fi toate numerele intre 1 si 9 sau literele de la A la Z. Un interval este creat prin plasarea unei serii de puncte intre limita superioara si cea inferioara a intervalului. De exemplu, daca ai fi creat un joc pe roluri si ai fi vrut sa setezi intervalele posibile ale inaltimii fiecarei rase (in inch) ai scrie:
om = 48..81
elf = 40...68
monstru = 120..132
Intervalele pot utiliza fie doua puncte, care indica un interval inchis ce contine valoarea initiala si pe cea finala, sau trei puncte, excluzand ultima valoare. Aceasta pare pe dos la prima vedere, dar poti gandi ca cel de-al treilea punct este atat de mare incat impinge ultimul element in afara intervalului. Nu glumesc; deschide un debugger si vei afla singur. De exemplu, intervalul 1...7 va crea un interval precum urmatorul:
Pe de alta parte, intervalul 1..7 va crea:
Acum ca poti aseza valorile corect intr-un interval, poate ca vrei sa si faci ceva cu ele. Intervalele ofera cateva metode pentru a le testa si compara. Mai intai, poti compara intervale utilizand operatorul == sau metoda eql?. Daca ar trebui sa scrii software pentru a administra vanzarile din panificatie (am auzit ca reprezinta o piata infloritoare in industria software acum), atunci poate vei dori sa scrii un cod de test pentru a verifica probabilitatea intervalului prajiturilor bune si rele pe care le poti obtine intr-un lot:
prajituri_bune = 1...3
prajituri_rele = 1..3
prajituri_arse = 1..3
puts(prajituri_bune == prajituri_rele) -> false
puts(prajituri_bune.eql?(prajituri_arse)) -> false
puts(prajituri_rele == prajituri_arse) -> true
Intervalele sunt considerate egale daca valorile lor initiale si finale sunt aceleasi, dar observa ca desi prajituri_bune si prajituri_rele au aceleasi valori initiale si finale in cod, valoarea lor difera. Valorile au fost schimbate de marcajul inclusiv (iti amintesti partea cu doua si respectiv trei puncte?). Valorile pentru prajituri_bune sunt [1,2] in timp ce prajituri_rele detin [1,2,3].
Intervalele ofera de asemenea modalitatea de a testa daca o valoare este sau nu continuta intr-un interval cu ajutorul operatorului === sau cu metoda include?. De exemplu, daca tu si prietenul tau ati ghicit un numar de prajituri bune, dar ati vrea sa vedeti daca numarul se afla in intervalul probabil de prajituri bune, ati putea face acest lucru astfel:
nr_meu = 2
nr_prietenului = 19
puts(prajituri_bune === nr_meu) -> true
puts(prajituri_bune.include?( nr_meu)) -> true
puts(prajituri_bune === nr_prietenului) -> false
Metoda include? va returna true pentru orice valoare care este cuprinsa in interval (de exemplu, daca ai fi testat 2.44564 pentru prajituri_rele s-ar intoarce true); Daca te simti putin schimbator, poti incerca un echivalent al metodei include?: member?.
Array-ul
Cea de-a doua colectie inclusa este array-ul sau vectorul, ce reprezinta un sir de elemente ordonate si indexate. Daca ai participat la un curs introductiv de informatica, conceptul de array nu ar trebui sa-ti fie strain, insa implemetarea Ruby poate sa-ti para usor nefamiliara. Spre deosebire de C/C++ si Java unde indexarea porneste de la 0 (de ex. primului element ii este atribuit indicele 0, celui de-al doilea element 1, si asa mai departe) si elementele dintr-un vector sunt de acelasi tip, elementele dintr-un array Ruby nu trebuie sa fie de acelasi tip; nici macar tipul de array nu trebuie specificat inainte de a fi initializat pentru utilizare. Deci, ai putea ajunge la un array asemanator acestuia:
In Ruby, pot fi create array-uri literale care pot fi populate in diverse moduri interesante si amuzante:
array_gol = []
alt_array_gol = Array.new
hello = ['ni hao', 'bonjour', 'hi', 'howdy']
tipuri_aleatorii = [13, 'napkin', (1336 + 1).to_s]
Un array poate fi initializat cu valori de orice tip, chiar si cu variabile, valori returnate de metode, literali cum ar fi siruri de caractere sau nimic (pentru a crea un array gol). Acest lucru este util mai ales pentru valorile literale, dar Ruby mai ofera cateva metode pentru crearea de array-uri care sunt mai convenabile si cu siguranta mai "Rubyrifice". Stringurile ofera un mod special de a crea array-uri din propriul continut. Sa presupunem ca scrii haiku si ca vrei sa te asiguri ca fiecare linie (care este convenabil umpluta cu cuvinte monosilabice) se potriveste tiparului "5-7-5" prin divizarea liniei intr-un array astfel incat sa poti numara elementele:
vers_haiku = %w( my dog digs it heren )
-> ["my", "dog", "digs", "it", "here" ]
vers_haiku = %w( he is nice to me & catsn )
-> ["he", "is", "nice", "to", "me", "&", "cats"]
vers_haiku = %W( but he ate #{(2*3)/6} once )
-> ["but", "he", "ate", "1", "once"]
vers_haiku = %w( but he ate #{(2*3)/6} once )
-> ["but", "he", "ate", "#{(2*3)/6}", "once"]
Oh! Un string infasurat in delimitatorul %W se comporta ca un sir cu ghilimele, adica efectueaza interpolarea stringului si substituie secventele escape, iar delimitatorul %w actioneaza ca un string cu apostrofi: permite utilizarea unui subset de secvente escape si nu faciliteaza interpolarea. Unii sunt zapaciti de toata aceasta vorbarie, dar este extrem de usor de retinut: este mai bine sa folosesti majuscula W (cu exceptia cazului in care nu ai nevoie de toate caracteristicile fantezie sau daca ai un fel de convingeri religioase impotriva ghilimelor duble si/sau a literei mari W).
Ultima modalitate de a forma array-uri pe care as dori sa o mentionez este metoda to_a a unor obiecte. Aceasta metoda converteste un obiect sau (rareori) pe unul dintre membrii sai intr-un array. De exemplu, intervalele suporta aceasta metoda:
intervalul_meu = 1..10
-> 1..10
array_din_interval = intervalul_meu.to_a
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Multe obiecte implementeaza aceasta metoda deoarece reprezinta un mod convenabil de a obtine o structura de date usor de manipulat din clase cu care este dificil de lucrat. Poti trage cu ochiul la documentatia Ruby API pentru a vedea daca obiectul pentru care vrei sa apelezi aceasta metoda o are implementata.
Acum, avand un array, poate ca doresti sa-i adaugi elemente. Elementele pot fi usor adaugate intr-un vector prin atribuirea unei valori unui indice non-existent; de exemplu:
array_din_interval[10] = 11
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
array_din_interval[12] = 12
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, nil, 12]
In cazul in care exista un decalaj intre indicii ultimului element din array si intre cel mai nou element, Ruby plaseaza valoarea nil (adica echivalentul lui null in alte limbaje de programare) in elementele ce nu au fost initializate (vezi array_din_interval[11] de mai sus). Daca vrei sa adaugi un element la capatul unui array, atunci poti utiliza operatorul <<, metoda push sau anumite forme ale metodei de inserare. De exemplu:
array_din_interval.push(15, 16)
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16]
array_din_interval.insert(-1, 17)
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 17]
array_din_interval << 14
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14]
Metoda push iti permite sa adaugi unul sau mai multe elemente la finalul unui array; fiecare element ar trebui sa fie furnizat ca parametru al metodei. Metoda insert iti permite sa introduci elemente la (indicele specificat + 1); in exemplu, am folosit -1 pentru a adauga elemente la sfarsitul array-ului (de ex. -1 muta un element de la capatul intervalului inapoi in ultima pozitie; adaugarea unui element dupa ultimul element l-ar adauga in mod efectiv la sfarsit). Probabil ca aceasta metoda nu este cea mai buna, dar poate fi utilizata in cazul in care aceeasi metoda trebuie sa introduca elemente in diverse locuri din array (inclusiv la sfarsit). Operatorul << iti permite sa impingi anumite elementele la capatul unui array; am pluralizat cuvantul element deoarece mai multe dintre aceste "adaugiri" pot fi legate impreuna pentru a adauga numeroase elemente la sfarsitul unui array. De exemplu:
array_din_interval << 20 << 21 << 22
-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22]
Acum ca ai date in array-ul tau, ce poti face cu ele? Mie imi place sa-mi admir datele, sa ma mandresc cu capacitatea mea de a valorifica tot ceea ce reprezinta un array si sa-mi arunc pumnul in aer strigand: "Eu sunt Spartacus, stapanul array-ului!". Apoi, sotia mea se uita la mine si spune ca sunt nebun, devine ciudat, si ma intorc la munca. S-ar putea sa functioneze si in cazul tau; cei mai multi oameni opteaza insa pentru utilizarea array-urilor pentru a pastra niste elemente si pentru a le apela atunci cand au nevoie (insa nu este la fel de distractiv). Pentru a utiliza valoarea unui element dintr-un array, faci pur si simplu referire la elementul dorit cu ajutorul indexului sau astfel: nume_array[index]. De exemplu:
puts array_din_interval[0]
-> 1
index = 2
array_din_interval[index]
-> 3
array_din_interval[0..2]
-> [1, 2, 3]
Observa ca atunci cand te referi la elementele array-ului, te poti referi la un singur element cu un index numar intreg sau poti folosi un interval pentru a te referi la un numar de elemente. Aminteste-ti, deoarece indexarea array-ului incepe de la 0, indicele de referinta 0 se refera de fapt la primul element. De asemenea, asigura-te atunci cand introduci un index ca este un numar intreg sau un interval, daca nu, Ruby va returna TypeError (care iti va zdrobi sufletul). Aceasta pare a fi o problema stupida (si o consecinta si mai stupida!), dar ar putea aparea in cazul in care (pentru un motiv oarecare), ai fi citit indecsii dintr-un fisier (care interpreteaza orice valoare ca un sir de caractere, astfel incat va trebui sa le convertesti cu ajutorul metodei pe care o vei invata mai tarziu). Aceasta metoda de referire la elementele unui array functioneaza asemenea metodei at:
puts array_din_interval.at(0)
-> 1
O alta metoda, fetch, poate opera in mod similar, dar fetch poate preciza de asemenea o valoare implicita pentru a fi returnata in cazul in care indicele specificat nu este gasit.
puts array_din_interval.fetch(999, "Element inexistent!!")
-> Element inexistent!!
O alta metoda, values_at opereaza similar cu at, fetch si operatorul [], dar, spre deosebire de acestea, poate primi ca parametri o serie de indecsi pe care ii cauta in array, returnand elementele corespunzatoare sub forma unui vector.
puts array_din_interval.values_at(0, 1, 2)
-> [1, 2, 3]
Ultima modalitate pe care vreau sa o impartasesc pentru extragerea de elemente este reprezentata de metodele pop si shift. Metoda pop preia ultimul element din array si il elimina; metoda shift extrage primul element din vector si il elimina, mutand toate celelalte elemente inapoi cu un index.
array_din_interval.pop
-> [1, 2, 3, 4, 5, 6, 7, 8, 9]
array_din_interval.shift
-> [2, 3, 4, 5, 6, 7, 8, 9, 10]
Deci, acum ai un array, ai date in el, dar poate ca ai o problema cu acel al treilea element si vrei sa-l elimini. Din fericire, Ruby se poate ocupa de aceasta mica problema si sa spunem ca nu este amabil. Metoda delete_at sterge elementul de la indicele specificat ca parametru si returneaza valoarea acelui element. De exemplu:
puts array_din_interval.delete_at(1)
-> 2
array_din_interval
-> [1, 3, 4, 5, 6, 7, 8, 9, 10]
O alta metoda pe care array-urile o ofera pentru stergerea elementelor este metoda delete (ce surpriza, nu?). Aceasta metoda sterge si returneaza valoarea la care se face referire ca parametru, mai degraba decat un index ca delete_at. De exemplu:
puts array_din_interval.delete(4)
-> 4
array_din_interval
-> [1, 2, 3, 5, 6, 7, 8, 9, 10]
puts array_din_interval.delete(1337) { "Gresit!" }
-> Gresit!
Retine ca elementul cu valoarea de 4 a fost sters si nu indicele 4. De asemenea, retine ca metoda delete ofera optiunea returnarii unei valori "standard" daca valoarea specificata nu exista in array; ultimul exemplu este o demonstratie a acestei facilitati. Blocul de cod delimitat de acolade este returnat daca elementul nu este gasit (voi vorbi mai pe larg despre aceste tipuri de blocuri de cod mai tarziu).
Cam atat despre colectii. In urmatoarea parte a acestui tutorial vei putea citi despre dictionare sau Hash si despre utilizarea variabilelor in Ruby.
|