Programski jezik
RUBY
NAJLAKŠI PROGRAMSKI JEZIK
----------------------------------------------© 2003-2009 Chris Pine http://pine.fm/LearnToProgram/
-1-
#. Uvod Predgovor prevodioca U ime Milostiv M ilostivog og i Svemilosnog Boga, U potrazi za stabilnim okruženjem koje će mi pomoći da razvijem open source program za obradu podataka na hematološkim klinikama i zavodima za transfuzijsku medicinu, naišao sam na Ruby, za svijet programiranja relativno nov jezik. Svi su ukazivali na jednostavnost ovog programskog jezika, a kako su moja prva iskustva u programiranju bila sa jezikom BASIC, za koji svi smijemo reći da je prilično jednostavan, jednostavan, pomislio sam kako bi dobro MySQL bazu povezati sa Ruby, ako je tako jednostavna i bio sam ubijeđen da sam pronašao savršenu platformu za moju buduću aplikaciju. Postoji danas mnogo različitih tutoriala, knjiga, uputstava i dokumentacije koja u glavi uglavnom proizvede vertigo – vrtoglavicu, a ne jasnu predstavu o programskom jeziku. Tutorial koji je napisao gospodin Kris Pajn (Chris Pine) je nešto drukčiji. On se postavlja prema onima koji tek uče Ruby kao da i on sam tek počinje s programiranjem...ustvari, programiranjem...ustvari, tutorial to i jeste – on je za početnike u programiranju, programiranju, one koji debituju, debituju, samo što im kao primarni jezik u programiranju postavlja Ruby. Iz tog razloga sam i tražio njegovu dozvolu da tutorial koji je napisao prevedem na bosanski jezik (što podrazumijeva podrazumijeva da ga razumije i veliki broj drugih Južnih Slavena, ne samo Bosanci i Hercegovci, nego Hrvati, Srbi, Crnogorci, Makedonci, Albanci, ...mnogi drugi, te Slovenci). Slovenci). Nisam našao nikakav sličan u našem govornom području (ustvari, nisam uopšte našao tutorial na nekom od jezika ex-YU naroda kojemu je tema Ruby ili uvod u programiranje – sličan konceptu kojeg Kris ima), pa sam pomislio kako bi bilo lijepo dati neki doprinos. Prijevod ovog tutoriala nije doslovan i izmjene u njemu imaju cilj da Ruby što bolje približe apsolutnom početniku. U par slučajeva (naslovima ili opisima), za neke definicije sam bio prinuđen koristiti izvorne nazive (koji potiču iz engleskog jezika), koji se inače koriste u programiranju, da bih opisao neke od elemenata elemenata sintakse koju koristi Ruby. Imajući u vidu da se budući programer zaista mora dobro snalaziti s engleskim jezikom, mislim kako izvorni nazivi neće predstavljati veći problem. Bude li među čitaocima ovog tutoriala onih koji mu nađu mahane, neka se slobodno jave, da eventualne nedostatke mogu što prije ispraviti. Dodatak: komentare prevodica, kojih je zaista malo, označavao sam [k.p.:] Rusmir Gadžo (
[email protected]) [http://sites.google.com/site/rubynabosanskom/] Februar] [Veljača 2009 / Safer 1430
Dobar početak za budućeg programera Sve je počelo negdje 2002 godine. Razmišljao sam kako bih mogao podučavati programiranju i kako bi divan jezik Ruby bio za to. Mislim, svi smo mi bili uzbuđeni kad je Ruby došao, jer smo uvidjeli kako je moćan, elegantan i stvarno jako zabavan jezik, ali meni se činilo da bi to takođe bio dobar jezik za one koji tek ulaze u programiranje. Nažalost, u to vrijeme nije bilo mnogo priručnika ili neke druge dokumentacije za Ruby početnike. Neki od nas, u programerskoj zajednici, pitali su se šta zaista treba da sadrži jedan „Ruby za početnika“ tutorial i kako pristupiti podučavanju programiranja uopšte. Što sam više razmišljao o ovome, više sam imao za reći (što me je pomalo iznenadilo). Konačno, neko mi je rekao „Kris, zašto jednostavno ne napišeš tutorial, umjesto što stalno pričaš o njemu?“ Tako sam i učinio. Nije mi išlo baš najbolje. Sve moje ideje su nekako bile dobre u teoriji, ali konkretan zadatak -2-
pisanja sjajnog tutoriala za one koji tek ulaze u programiranje bio je priličan izazov, veći nego sam zamišljao. (Meni se tutorial činio dobrim, ali ja sam već poznavao programiranje.) Spasilo me je to što sam olakšao ljudima kontakt sa mnom, pa sam im tako mogao pomoći svaki put kad bi nešto „zapelo“. Kad bih vidio da mnogo njih „zapinje“ na jednom (istom) mjestu, onda bih se potrudio da taj dio izmjenim, napišem ponovo. Bilo je tu dosta posla, ali je lagano postajalo sve bolje i bolje. Par godina nakon toga, postao je to jedan stvarno dobar tutorial J. Ustvari, tako dobar, da sam bio spreman proglasiti ga završenim, tako da bih mogao preći na nešto drugo. Upravo tada se ukazala prilika da se moj tutorial pretvori u knjigu. Pošto je, u suštini, bio završen, nisam imao ništa protiv da se objavi kao knjiga. Jednostavno bih dodao par novih tačaka, par novih vježbi, možda koji primjer više, par novih poglavlja, pronašao 50-tak ljudi za recenziju... Radio sam na tome još jednu godinu, ali sada mislim da je tutorial jako dobar, većinom iz razloga što mi je u pomoć priteklo stotine hrabrih duša. Ono što stoji ovdje je originalni tutorial, manje-više izmjenjen od 2004. godine. Za ono najbolje i najnovije, trebalo bi pogledati kako izgleda moja knjiga knjiga..
Par savjeta za predavače Postoji nekoliko principa kojih sam se pokušao držati dok sam pisao ovaj tutorial. Mislim da ti principi čine da proces učenja programskog jezika teče lakše; učiti programiranje je ionako teško, samo od sebe. Ako ste od onih koji nekoga podučavaju kako uploviti u vode „hackera“ onda bi i vama ove ideje mogle biti od pomoći. Prvo, pokušao sam razdvojiti koncepte, što je više moguće, tako da bi učenik učio samo jedan koncept. Ispočetka je išlo teško, ali nakon malo vježbe bilo je i previše lahko. Neke se stvari moraju naučiti prije onih na koje dolazimo, ali sam bio iznenađen što je zaista vrlo malo hijerarhije pređašnjosti (poznavanja prethodnog gradiva) bilo uključeno. Na kraju, trebalo je samo da poredam stvari, a pokušao sam da ih uredim tako da bi novi naslovi bili motivirani motivirani onim prošlim. Još jedan od principa kojih sam se držao je podučiti ljude da postoji samo jedan način da nešto naprave. Očigledna je to beneficija za ljude koji nikad nisu programirali. Jedno je sigurno, jedan način da se nešto napravi je lakše naučiti nego dva. Možda je važnije to da, što manje stvari naučite programera, to će više do izražaja doći njegova kreativnost i pamet u programiranju. Iz razloga što se većina stvari u programiranju svodi na riješavanje riješavanje problema, od ključne je važnosti da se budući programeri što više potiču na to u svakoj fazi podučavanja. Pokušao sam uključiti neke programerske koncepte u koncepte koje ljudi već imaju kod sebe, u životu, da bih prezentovao ideje na takav način da je njihova intuicija ono na što će se osloniti, a ne tutorial. Objektno-orijentisano programiranje se jako dobro uklapa u ovakav koncept. Zbog toga sam vrlo rano mogao da počnem sa naglašavanjem „objekata“ i različitih „vrsta objekata“ , izgovarajući te fraze bez ustručavanja. Nisam koristio izraze kao što su „sve u Ruby je objekat“ ili „brojevi i znakovi su vrste objekata“, iz razloga što što ovi izrazi ne znače ništa onome ko tek stupa u programiranje. Umjesto toga, pričao bih samo o znakovima (ne „znakovnim objektima“), a ponekad bih o objektima govorio samo kao „stvarima“ u nekom od programa. To što sam, pomalo podmuklo J, rekao za objekte u Ruby da su „stvari u programima“, imalo je velikog uspjeha. Kada bih svoje učenike podučavao, pokušao bih da izbjegnem izbjegnem nepotrebni žargon, pa ako bi trebali naučiti neki od naziva, onda bih se potrudio da to bude onaj pravi; tako sam znakove -3-
nazivao „strings“, „strings“, a ne tekstom. I metode sam nekako morao nazvati, pa kako bih drukčije nego „methods“. Što se vježbanja tiče, mislim da sam izmilslio par vježbi koje su jako dobre, ali njih svakako nikad nije dosta. Iskreno, mislim da sam pola svoga vremena potrošio samo smišljajući zabavne, zanimljive vježbice. Dosadna vježba ubija želju za programiranjem, dok savršena vježba kod novopečenog programera pravi svrab koji jednostavno jednostavno mora počešati! Ukratko, kad se prave dobre vježbe, tu ne možete nikad provesti previše vremena. vremena.
O originalnom tutorialu Stranice ovog tutoriala generiše jedan poveći program napisan u Ruby, naravno :). Kao takav, ima par zanimiljivih osobina. Na primjer, svi primjeri programskog koda izvršavaju se svaki put kad se stranica učita i prikazani ispis je ispis koji program generiše. Mislim da je ovo najbolji i najlakši način da se osigura da sav kod koji prezentiram radi tačno onako kako ja to kažem. Ne morate se brinuti da li sam kopirao ispis nekog od primjera pogrješno ili zaboravio testirati dio koda; svaki put kad ga čitate, on istovremeno bude i testiran (odnosi se na webiste autora: http://pine.fm/LearnToP http://pine.fm/LearnToProgram/). rogram/). Tako, na primjer, u odjeljku koji govori o generisanju slučajne cifre, svaki put kad učitate stranicu, imaćete priliku da vidite novi broj...zgodno! broj...zgodno! (Koristio sam isti trik i u kodu koji sam dao kako primjer kad sam pisao knjigu, ali očigledno je da se efekti daju lakše uočiti u ovom tutorialu.)
Priznanja Konačno, htio bih da se zahvalim učesnicima ruby-talk mailing liste na njihovim zamislima, prijedlozima i ohrabrenjima, svim mojim divnim recenzentima na njihovoj pomoći da knjigu učinim boljom nego bi ona bila kad bih je pisao ja sam, mojoj voljenoj supruzi, jer je bilo moj glavni recenzent/tester/pokusni kunić/muza , Matzu jer je napravio ovaj sjajni jezik i Pragmatic Programmers jer su me s njim upoznali i naravno, objavili moju knjigu! Ukoliko uočite kakve grješke ili imate komentar ili prijedlog, možda dobru vježbu koju bih mogao uvrstiti, slobodno to podijelite sa mnom putem ove adrese: e-mail
0. Da počnemo... Programirati na računaru, to znači "govoriti" jezikom koji naš računar razumije: programskim jezikom. Danas je u opticaju opticaju mnogo različtih programskih programskih jezika i među njima ima onih koji su izvrsni. Za ovaj tutorial, odabrao sam moj omiljeni programski jezik, Ruby. Osim što je moj omiljeni, Ruby je takođe i najjednostavniji programski jezik koji sam ikad vidio (a vidio sam stvarno dosta njih u karijeri). Ustvari, to je razlog zbog kojeg pišem ovaj tutorial: nisam odlučio napisati ga i odabrati Ruby samo zato što je moj omiljeni; naprotiv, otkrio sam da je Ruby jako jednostavan programski jezik i da bi za njega trebao postojati dobar tutorial za početnike. Dakle, jednostavnost Ruby kao programskog jezika potakla je pisanje ovog tutoriala, a ne činjenica da je to moj omiljeni jezik. (Pisanje sličnog tutoriala za neki drugi programski jezik, kao što je Java ili C++, zahtijevalo bi par stotina stranica). Nemojte pomisliti da je Ruby početnički jezik iz razloga što je tako jednostavan. Ruby je moćan programski jezik, čija jačina u profesiji je možda najveća do sada viđena. Kad pišete nešto što je ravno govoru čovjeka, onda to nazivamo tekstom. Kad pišete nešto što je ravno govoru računara, onda već govorimo o nečemu što se u profesiji programera naziva naziva kod. U ovaj sam tutorial uvrstio dosta primjera Ruby koda, a većina tog koda su kompletni programi koje i sami možete pokrenuti na svom računaru. Da bih olakšao čitanje koda, obojio sam neke njegove dijelove različitim bojama. (Npr., brojevi -4-
imaju uvijek zelenu boju). Sve što će biti unijeto putem tastature (input) biće na bijeloj, a sve što će program dati kao rezultat biće na plavoj podlozi (output). Ukoliko naiđete na dijelove koje ne razumijete, ili imate pitanje na koje nije ponuđen odgovor, zapišite to pitanje i nastavite čitati! Moguće je da će taj odgovor doći sam u nekom od narednih poglavlja. Međutim, ako odgovor ne dobijete do posljednjeg poglavlja, spreman sam vam pomoći da ga nađete. Mnogo je divnih ljudi koji su spremni da pomognu, samo treba znati gdje se oni nalaze. No, prvo što trebamo učiniti je - instalirati Ruby na računar.
INSTALACIJA NA WINDOWS OPERATIVNI SISTEM Instalirati Ruby na Windows operativni sistem je prilično lahko. Prvi na listi zadataka je download Ruby installera. Vjerovatno ćete naići na nekoliko različitih verzija, dostupnih za download; ovaj tutorial koristi verziju 1.8.4, tako da bi bilo dobro da i vaša instalacija bude makar u nivou te verzije. (Najbolje bi bilo odabrati posljednju – najnoviju verziju). Nakon što je download završen, pokreće se najobičnija instalacija, kao i za većinu Windows aplikacija. Tokom instalacije ćete biti upitani u koji folder/direktorij želite smjestiti Ruby. Ruby je najbolje smjestiti u „default location“, osim ako vi nemate neki poseban razlog zbog kojeg to ne želite učiniti. Da biste mogli programirati, potrebni su vam alati koji će omogućiti pisanje koda i njegovo izvršavanje. Za sve to će biti potreban program za uređivanje teksta i „komandna linija“. Ruby installer dolazi sa dražesnim uređivačem teksta nazvanim SciTE (the Scintilla Text Editor). Možete ga pokrenuti iz „Start“ menija. Ukoliko želiti imati obojen kod, kao što je to u ovom tutorijalu, onda postavite fajlove koje ću vam navesti ispod u SciTeov folder/direktorij (ako ste Ruby instalirali u „default location“ onda je putanja do ovog foldera: c:/ruby/scite ). Global properties Ruby properties Ne bi bilo loše imati negdje zaseban folder u kome će biti smješteni programi, odnosno kod koji napišete. Kad završite s programiranjem, pobrinite se da program koji ste napisali spasite u taj folder/direktorij. folder/direktorij. Komandnu liniju najjednostavnije je koristiti preko integrisanog „Command prompt“-a koji je integrisan u sam Windows, a može ga se naći u Start->All programs>Accessories . Tipične komande „cd“ i „dir“ daće vam uvid u putanju u kojoj se trenutno nalazite, a možete ih koristiti i da dođete do foldera/direktorija u kome su smješteni programi koje ste napisali. napisali. To bi bilo sve! Sad ste već spremni učiti programiranje.
INSTALACIJA NA MAC OPERATIVNI SISTEM Ukoliko radite na Mac OS X 10.2 (Jaguar) operativnom sistemu, onda je Ruby već instaliran na vaš računar. Je l' moglo biti lakše? Mislim da, nažalost, na verzijama sistema manjim od ove (uključuje 10.1 i manje) Ruby ne dolazi instaliran. Da biste mogli programirati, potrebni su vam alati koji će omogućiti pisanje koda i njegovo izvršavanje. Za sve to će biti potreban program za uređivanje teksta i „komandna linija“. Komandna linija dostupna je preko Terminala (koji se nalazi u Applications/Utilities ). Uređivača teksta što se tiče, možete koristiti bilo koji, koji vama lično pogoduje. Ukoliko koristite TextEdit, budite sigurni da programe koje napišete spasite spasite kao „text-only“, „text-only“, u protivnom vaši programi neće raditi. Alternativne opcije su emacs, vi ili pico i svi su dostupni preko komandne linije. -5-
To bi bilo sve! Sad ste već spremni učiti programiranje. INSTALACIJA NA LINUX OPERATIVNI SISTEM
Prije svega, treba provjeriti da li je Ruby već instalirana na distribuciju Linux operativnog sistema kojeg koristite. Ukucajte u komandnu liniju (terminal) „ which ruby“. Ukoliko kao odgovor dobijete nešto kao „ /usr/bin/which: no ruby in (...) “, onda morate obaviti download download.. U suprotnom, provjerite provjerite koju verziju Ruby vaša distribucija uključuje komandom „ with ruby –v “ . Ukoliko je starija od posljednjeg stabilnog izdanja koje je dostupno, onda bi bilo lijepo napraviti upgrade. Ukoliko imate privilegije „ root “ korisnika, onda vam vjerovatno ne trebaju posebne upute za instalaciju Ruby. Međutim, ako to nije slučaj, onda ćete tražiti od administratora vašeg sistema da to učini za vas (na taj način bi svi koji pristupaju tom sistemu bili u mogućnosti da ga koriste). Ukoliko želite instalirati Ruby samo za određenog korisnika, onda usmjerite dowloadiranu instalaciju u neki trenutni folder/direktorij, folder/direktorij, npr. $HOME/tmp. Ako je ime fajla koji ste downloadirali ruby-1.6.7.tar.gz , onda ga ruby-1.6.7.tar.gz “. možete otvoriti komandom „ tar zxvf ruby-1.6.7.tar.gz “. Pristupite folderu/direktoriju koji ste upravo napravili za tu svrhu ( $HOME/tmp – „tmp“ ime koje ste mu odlučili dati) komandom „cd tmp“). Konfigurišite prvo instalaciju komandom „ ./configure –prefix=$HOME “. Nakon toga u komandnu liniju upišite „make“,...ona bi trebala izgraditi vaš Ruby interpreter...što interpreter...što bi moglo potrajati koji minut. Po završetku tog procesa unesite komandu „ make install “, “, da bi instalacija konačno bila izvršena. Poslije instalacije bi trebalo dodati „ $HOME/bin“ folder/direktorij u tražilicu komandne linije editovanjem „$HOME/.bashrc“ fajla. (Da bi izmjene bile prihvaćene, potrebno je uraditi logout i nakon toga ponovo ući u sistem). Po ponovnom pristupanju sistemu (login), testirajte je li instalacija uspjela komandom „ ruby –v“. Ukoliko u outputu dobijete verziju Ruby, znači da je instalacija bila uspješna i da $HOME/tmp folder/direktorij (ili kako god ga vi nazvali) možete slobodno brisati. To bi bilo sve! Sad ste već spremni učiti programiranje.
-6-
1.Brojevi Sad kad je sve podešeno, instalacija izvršena, hajde da napišemo program. Otvorite svoj omiljeni uređivač teksta i upišite slijedeće: puts 1+2
Spasite program (da, da, to je program!) kao calc.rb (*.rb je ekstenzija koju obično stavljamo programima napisanim u Ruby). Pokrenite sada taj program upisujući calc.rb u komandnu liniju. Na ekranu bi se kao rezultat trebao ispisati “3 “ 3”. Vidite, programiranje i nije tako teško, zar ne? Predstavljamo komandu „puts“
Dakle, šta program koji smo napisali radi? Siguran sam da ćete od prve pogoditi šta će biti rezultat komande 1 + 2; naš je program, u suštini, isti kao: puts 3
„ puts“ dakle, ispisuje na ekran sve ono što se nalazi ispred njega.
Cijeli i decimalni brojevi U većini programskih jezika (Ruby nije nikakva iznimka) iznimka) brojevi bez decimale nazvani su „ integers“, a brojevi sa decimalnim zarezom nazvani su „ floating-point“ ili jednostavnije „ floats“. Evo primjera par integers: 5 -205 999999999 0 A tu je i par floats: 54.321 0.001 -205.3884 U praksi, većina programa ne koristi floats, samo integers (uostalom, ko još želi pregledati 7.4 e-mail, ili 1.8 web stranica ili slušati 5.24 omiljenih pjesama). Floats se koriste više za akademske svrhe (pokuse u fizici i sl.) i za 3D grafiku. Čak i većina programera koji rade aplikacije koje upravljaju novcem koriste integers; siću prate cjelobrojnim vrijednostima! Jednostavna aritmetika
Do sada smo uspjeli napraviti najjednostavniji kalkulator. (Kalkulatori uvijek koriste floats, pa tako, ako želite napraviti digitron od svog računara, trebali biste takođe koristiti isti). Kao što smo mogli vidjeti, za sabiranje i oduzimanje se koriste znakovi [+] i [-]. Kod većine tastatura/tipkovnica, ovi su dugmići smješteni krajnje desno, a ukoliko imate laptop onda je to kombinacija Shift+8 i Shift+/ (za: english keyboard layout). Hajde da sad malo proširimo naš calc.rb program. Dodajte u program slijedeće linije i onda ga pokrenite: puts puts puts puts
1.0 2.0 5.0 9.0
+ * /
2.0 3.0 8.0 2.0
Ono što će program vratiti kao ispis biće:
-7-
3.0 6.0 -3.0 4.5
(Razmaci koji se vide u programu – između rezervisanih riječi i vrijednosti – nisu od važnosti; oni samo pomažu da se kod lakše čita.) Dobro, to i nije bilo baš iznenađujuće. Hajde da pokušamo sa integers: puts puts puts puts
1+2 2*3 5-8 9/2
Uglavnom isto, zar ne? 3 6 -3 4
Pardon…osim onog zadnjeg! Ali, tako vam je kad radite aritmetiku sa integers - dobijete i cjelobrojnu vrijednost. vrijednost. Kad vaš računar me može da nađe “pravi” odgovor, onda ga on uvijek zaokruži. (Naravno, za cjelobrojnu aritmetiku, 9/2 = 4 je tačan odgovor, iako možda i nije onaj koji smo očekivali.) Pitate se vjerovatno, za kakvu je svrhu dijeljenje integerom dobro. Recimo da idete u kino, ali imate samo 9$. Ovdje u Portlandu, možete pogledati film u “ Bagdadu ” za dva dolara. Koliko to onda filmova možete pogledati? 9/2…4 filma. Četiri i pola sigurno nije tačan odgovor u ovom slučaju; neće vas baš pustiti da pogledate polovinu filma, ili pak dozvoliti polovini vas (kao čovjeka ili žene) da pogledate cijeli film… neke stvari jednostavno nisu djeljive na taj način. Sad već imate slobodu da sami pišete svoje programe! Ako želite uvrstiti malo složenije izraze, koristite zagrade. Evo par primjera: puts 5 * ( (12 12-8) + -15 -15 rezultira: 5 puts 98 + ( (59872 59872 / ( (13 13* *8)) * -52 -52 rezultira: -29802
Par stvarčica za probati… Napišite program koji će vam reći: koliko sati ima u jednoj godini? koliko minuta ima u jednoj dekadi (deset godina)? koliko ste sekundi stari? koliko čokolada se nadate da bi mogli pojesti u životu? • • • •
Evo malo težeg pitanja: Koliko sam star, ako imam 1025 miliona sekundi života? •
-8-
Ukoliko ste završili igru s brojevima, onda možemo preći na slova.
2. Slova Dakle, naučili smo ponešto o brojkama, ali šta je sa slovima, riječima, tekstom? Grupu znakova u jednom programu nazivamo skupnom oznakom „ strings“ (znakovni niz). (Možete ih zamisliti kao slova štampanim na nekoj reklami.) Da jednostavnije objasnimo koji dio kod pripada stringu, bojićemo ih u crveno. Slijedi par primjera: 'Hello. Hello.' ' 'Ruby rocks.' rocks.' '5 is my favorite number... what is yours? yours?' ' 'Snoopy says #%^?&*@! when he stubs his toe.' toe.' ' ' ''
Kao što možete vidjeti, strings mogu imati ne samo slova nego i ostale znakove. Poslijednji primjer, u kome nema nikakvih znakova nazvali bismo praznim ili “ empty string”. Koristili smo naredbu „ puts” za ispis brojeva, hajde da pokušamo isto i sa strings: puts 'Hello, 'Hello, world!' world!' puts '' puts 'Good-bye. 'Good-bye.' '
Ispis bi izgledao ovako: Hello, world! Good-bye.
To je dobro ispalo. Pokušajte sami napisati nekoliko primjera, proizvoljno.
Aritmetika znakovnog niza (string) Baš onako kako izvodimo aritmetičke operacije nad brojevima, moguće ih je izvoditi i nad slovima, odnosno znakovima (strings). Pa, dobro,...nekako već možemo dodati stringove jedne drugima. Hajde da pokušamo dodati jedan string drugome, pa da vidimo kakav će ispis na ekranu biti. puts 'I 'I like' like' + 'apple 'apple pie.' pie.'
Ispis: I likeapple pie. -9-
Opaaa! Zaboravih napraviti razmak između „I like” i „apple pie”. Razmaci uglavnom nisu toliko važni, generalno, ali su važni unutar stringa. (Istina je ono što pričaju: računari ne rade ono što mi od njih želimo da urade, nego ono što im kažemo/naredimo.) kažemo/naredimo.) Hajde da pokušamo ponovo: puts 'I 'I like ' + 'apple 'apple pie.' pie.' puts 'I 'I like like' ' + ' apple pie.' pie.'
Ispis: I like apple pie. I like apple pie.
(Kao što možete uočiti, nije važno kojem stringu sam dodao razmak.) Dakle, moguće je „adirati” znakove jedne drugima, ali je takođe moguće i množiti ih! (Uvećati ih za neki broj puta). Vid' ovo: puts 'blink 'blink ' * 4
Ispis: batting her eyes (...ona trepće očima)
(Šalim se, ono što bi se na ekranu ispisalo je:) blink blink blink blink
Savršeno je smislen ispis, ako malo razmislite. Uostalom, 7 * 3, znači jednostavno 7+7+7, tako da bi i 'moo'+3 značilo 'moo'+'moo'+'moo'.
12 protiv '12' Prije nego krenemo dalje, treba se uvjeriti da razlikujemo brojeve i brojke kao znakove. 12 je broj kao numerička vrijednost, a '12' je string sastavljen od dva znaka – dvije brojke. Hajde da se malo poigramo ovim razlikama: puts 12 + 12 puts '12 '12' ' + '12 '12' ' puts '12 '12 + 12' 12'
Ispis: 24 1212 12 + 12
A šta kažete na ovo: -10-
puts 2 * 5 puts '2 '2' * 5 puts '2 '2 * 5' 5'
Ispis: 10 22222 2 * 5
Ovi su primjeri prilično izravni. Međutim, ako niste pažljivi kako miješate string i brojeve, mogu se javiti i…
Problemi Do ovog momenta je vjerovatno bilo stvari koje ste pokušali, pa nisu ispale kako treba. Ukoliko to nije bio slučaj, evo par primjera: puts '12 '12' ' + 12 puts '2 '2' * '5 '5' #
Hmmm…u ispisu se pojavljuje pojavljuje poruka da je došlo do grješke. Problem je, ustvari, što ne možete sabrati broj (integer, float) i znakovni niz (string), ili pomnožiti dva znakovna niza (string). Takve operacije nemaju smisla, kao što nemaju ni ove: puts 'Betty 'Betty' ' + 12 puts 'Fred 'Fred' ' * 'John 'John' '
Postoji još nekoliko stvarčica na koje morate obratiti pažnju: možete u program napisati 'žaba' * 5, pošto to znači da će string 'žaba' biti umnožen pet puta. Međutim, ne možete napisati 5 * 'žaba', jer bi to značilo da je broj pet umnožen žaba puta,...što je baš smiješno [k.p.:...ili kako kod nas kažu: „ne idu babe i žabe“]. Konačno, šta ako želim da moj program napiše „You're swell“? Možemo pokušati ovako: puts 'You're swell!'
Ali, to baš i neće ispasti dobro; nećemo čak ni pokušavati. Računar je pomislio da smo gotovi s pisanjem stringa. [k.p.: računar prepoznaje string kao onaj niz znakova koji je zatvoren između dva navoda ' *** ']. (Iz tog je razloga poželjno imati uređivač teksta koji će prikazivati obojenu sintaksu programskog jezika.) Kako onda da kažemo programskom jeziku kako treba da izgleda naš string? Moramo izbjeći prerano prekidanje navoda ovako: puts 'You\'re 'You\'re swell!' swell!'
Ispis: -11-
You're swell!
Kosa crta (nadesno) je rezervisani znak za izbjegavanje/poništavanje prekida navoda stringa. S druge strane, ako imate kosu crtu i neki drugi znak uz nju, onda su ti znakovi nekad prevedeni u sasvim novi znak. Jedino što kosa crta poništava su navodi i sama kosa crta. (Ako malo bolje rasmislite, znakovi za poništavanje uvijek bi morali poništavati sami sebe.) Slijedi nekoliko primjera: puts 'You\'re 'You\'re swell!' puts 'backslash at the end of a string: \\' \\' puts 'up\\down 'up\\down' ' puts 'up\down 'up\down' '
Ispis: You're swell! backslash at the end of a string: \ up\down up\down
Pošto kosa crta ne poništava „d“ u riječi 'down', nego samu sebe, posljednja dva primjera su identična. Ne izgledaju isto u kodu, ali na vašem računaru su oni stvarno jednaki. Ukoliko već sada imate nekih pitanja, nastavite čitati slijedeće poglavlje. Na ovoj stranici nisam mogao dati baš sve odgovore odjednom.
3. Promjenjive i dodijeljivanje Do ovog momenta, kad god bismo komandu „ puts“ koristili da bi ispisali neki string ili broj, nakon što je ona izvršena, taj string ili broj bi nestao. Htio sam reći da, ako bismo htjeli ispisati ispisati (output) neki string dva puta, onda bismo ga morali dva puta i otkucati: puts '...you '...you can say that again...' again...' puts '...you '...you can say that again...' again...'
Ispis: ...you can say that again... ...you can say that again...
Bilo bi lijepo kad bi to mogli negdje upisati jednom i onda ga i zadržati,...spasiti ga negdje. Pa, naravno da je to moguće, inače ne bih ni spominjao. Da bi smjestili neki string u memoriju računara, potrebno je označiti taj cijeli string nekim imenom. Programeri najčešće ovaj proces opisuju kao dodijeljivanje (assignment), a ime koje će imati taj string nazivaju varijablom. Samu varijablu možemo označiti bilo kakvim nizom znakova, ali prvi znak mora biti malo slovo. Da pokušamo ovaj posljednji program napisati još jednom, ali ovaj put string koji smo pisali iznad, označit ćemo varijablom imena myString (iako samo joj mogao dati i neko sasvim drugo ime, str ili myOwnLittleString ili henryTheEight ).
-12-
myString = '...you '...you can say that again...' again...' puts myString puts myString
Ispis: ...you can say that again... ...you can say that again...
Svaki naredni put kad se pozovete na myString, program će umjesto toga koristiti string „ ...you can say that again... ”. Možete sebi predstaviti varijablu myString kao nešto što upućuje na string „ ...you can say that again... ”. Evo još nekoliko zanimljivih primjera: name = 'Patricia 'Patricia Rosanna Jessica Mildred Oppenheimer Oppenheimer' ' puts 'My 'My name is ' + name + '. '.' puts 'Wow! 'Wow! ' + name + ' is a really long name!' name!'
Ispis: My name is Patricia Rosanna Jessica Mildred Oppenheimer. Wow! Patricia Rosanna Jessica Mildred Oppenheimer is a really long name!
Kao što možemo varijabli dodijeliti ( assign) neki objekt, tako taj objekat možemo zamijeniti nekim drugim, unutar iste varijable ( reassign). (To i jeste razlog što ih nazivamo promjenjive; ono čemu one teže može varirati, mijenjati se.) composer = 'Mozart 'Mozart' ' puts composer + ' was "da bomb", in his day.' day.' composer = 'Beethoven 'Beethoven' ' puts 'But 'But I prefer ' + composer + ', ', personally.' personally.'
Ispis: Mozart was "da bomb", in his day. But I prefer Beethoven, personally.
Naravno, kao varijablu možemo definisati bilo koji objekt, ne samo string: var = 'just 'just another ' + 'string 'string' ' puts var var = 5 * ( (1 1+2) puts var
Ispis: just another string 15 -13-
Ustvari, varijable mogu ukazivati na gotovo sve,...osim drugih varijabli. Međutim, šta se desi ako bismo to ipak pokušali? var1 var2 puts puts
= 8 = var1 var1 var2
puts '' var1 = 'eight 'eight' ' puts var1 puts var2
Ispis: 8 8 eight 8
Dakle, prvo smo ukazali var2 na var1 , što je toj varijabli dodijelilo objekat koji je definisan prvom varijablom i dalo rezultat 8. Onda smo dodijelili varijabli var1 drugi objekat (izvršili reassign) „eight“, ali pošto ustvari var2 nikad nije bila vezana za var1 [k.p.: što znači da se var2 ne mijenja dimanički, ne prati promjene na var1], njena je vrijednost ostala 8. Eh sad kad smo naučili nešto o varijablama, brojevima i stringovima, hajde da naučimo i kako sve njih da pomiješamo pomiješamo u jednom programu!
4. Haj' da malo pomiješamo Vidjeli smo par različitih objekata (brojevi i slova), a onda definisali i par varijabli da ukažemo na te objekte; slijedi nam dio u kome ćemo pokušati da sve navedeno lijepo uklopimo u jedan program. Moći ćemo primjetiti da, ako želimo da program ispiše 25, kod koji je napisan ispod neće raditi, jer se ne mogu sabirati brojevi i slova: -14-
var1 = 2 var2 = '5 '5' puts var1 + var2
Dio je problema i to što računar ne može raspoznati da li želimo dobiti rezultat 7 (2 + 5) ili je 25 rezultat koji smo željeli. Prije nego budemo u mogućnosti sabrati ova dva objekta, treba nam ili string verzija var1 ili integer verzija var2.
Pretvorbe (konverzije) Za pretvaranje bilo kojeg objekta u string koristimo dodatak .to_s na kraju neke varijable: varijable: var1 = 2 var2 = '5 '5' puts var1.to_s + var2
Ispis: 25
Slično prethodnom primjeru, dodatak .to_i rezultiraće integer verzijom objekta definisanog tom varijablom, a prateći istu logiku .to_f će pretvoriti objekat vezan za neku varijablu u float. Pogledajmo sada pobliže šta ove tri metode mogu, a šta ne mogu učiniti: var1 = 2 var2 = '5 '5' puts var1.to_s + var2 puts var1 + var2.to_i
Ispis: 25 7
Primjetit ćete da, čak i nakon što smo dobili string verziju var1 pozivajući .to_s metodu, var1 uvijek ukazuje na 2, a nikad na '2'. Ukoliko joj ne dodijelimo novu vrijednost/objekat ( reassign ) (za što nam je potreban znak '='), var1 će tokom cijelog vremena izvršenja programa ostati 2. Hajde sada da probamo nekoliko zanimljivih zanimljivih (ali i nekoliko čudnih) primjera pretvorbe: puts puts puts puts puts
'15 '15'.to_f '.to_f '99.999 '99.999'.to_f '.to_f '99.999 '99.999'.to_i '.to_i '' '5 '5 is my favorite number!'.to_i number!'.to_i -15-
puts puts puts puts puts
'Who 'Who asked you about 5 or whatever? whatever?'.to_i '.to_i 'Your 'Your momma did.'.to_f did.'.to_f '' 'stringy 'stringy'.to_s '.to_s 3.to_i
Ispis: 15.0 99.999 99 5 0 0.0 stringy 3
Ispis koji vidimo vjerovatno je proizveo iznenađenje. Prvi primjer je standardno, proizveo proizveo 15.0. Nakon toga smo pretvorili string '99.999' u float, pa u integer. Pretvorba u float je rezultirala očekivano; integer je, kao i obično, zaokružio vrijednost. Nadalje, imali smo primjere u kojima je par neuobičajenih stringova pretvoreno u brojeve. Metoda .to_i ignoriše stvari koje ne može razumjeti, na način da prekida konverziju na mjestu gdje se „nerazumijevanje“ prvi put pojavi i nastavi sve do kraja [k.p. : iz tog razloga u ovom primjeru počinje ignorisati ignorisati vrijednosti poslije 5 i nastavlja to do kraja stringa]. Tako je prvi znak pretvoren u 5, a ostali znakovi, obzirom da su počinjali slovima, ignorisani su u potpunosti,...računar je za njih odabrao samo nule. Konačno, vidimo naše poslijednje dvije konverzije - nisu nimalo neočekivane.
Još jedan osvrt na „puts“ metodu Nešto mi je čudna naša omiljena metoda – puts -,...pogledajte -,...pogledajte samo ovaj primjer: puts 20 puts 20 20.to_s .to_s puts '20 '20' '
Ispis: 20 20 20 Zašto sve tri metoda daju isti ispis? Dobro, posljednje dvije bi i trebale, obzirom da je 20 .to_s jednako '20'. Ali, šta je s prvim primjerom, brojem 20? Kad smo već kod toga, šta uopšte znači ispisati broj 20 (kao integer)? Kad napišete dvicu i nulu na komad papira, onda pišete string, a ne vrijednost broja koji bi nastao kad bi se pridružile te dvije cifre. Broj 20 je broj (vrijednost) prstiju na rukama i nogama, nije to dvica praćena nulom. E pa, otkrit ću vam veliku tajnu o našem prijatelju – puts-. Prije nego ova metoda da bilo kakav ispis -16-
objekta, ona koristi metodu .to_s da bi dobila string verziju objekta koji smo joj stavili u zadatak za ispisati. Ustvari, ono „S“ koje vidimo na kraju naziva ove metode, znači STRING; tako metoda – puts- znači PUT STRING. Za ovu činjenicu se možda trenutno ne mora vezati puno uzbuđenja, ali postoji u Ruby veliki broj različitih objekata (a naučićete kako da napravite i sami svoje objekte!), te je zato lijepo znati šta će se desiti ako pokušate „putsovati“ neke malo čudnije objekte kao što su slike vaših nana ili neka omiljena pjesma. Ali,...to će doći malo kasnije... U međuvremenu, upoznaćemo se sa par novih metoda, koje će nam dozvoliti da napišemo dosta zanimljivih programčića...
Metode „gets“ i „chomp“ Rekosmo da – puts puts- slovi za put string. Siguran sam da ćete lahko pogoditi šta onda – gets- znači. Baš kao što –puts- svaki put daje ispis stringa, tako – gets- uzima unose u obliku stringa. Uzima od vas! Ma dobro, od tastature/tipkovnice. Pošto tastatura/tipkovnica „skuplja“ samo znakove (string), to u slučaju ove metode stvari teku savršeno. Ono što se ustvari dešava je da – gets- ustvari „čuči“ i čeka sve one znakove koje prospete preko tastature/tipkovnice sve dok ne pritisnete Enter. Hajde da pokušamo: puts gets
Ispis: Is there an echo in here? Is there an echo in here?
Naravno, u ispisu bi bilo ono što biste vi unijeli nakon što je program pokrenut. Pokrenite program nekoliko puta, pa upišite različite unose. Sad već možemo krenuti u pisanje programa koji će donijeti malo više interakcije. U primjeru koji slijedi, upišite svoje ime u program i on će vas pozdraviti: puts name puts puts
'Hello 'Hello there, and what\'s your name?' name?' = gets 'Your 'Your name is ' + name + '? '? What a lovely name!' name!' 'Pleased 'Pleased to meet you, ' + name + '. '. :)' :)'
Hop! Nakon što sam pokrenuo program dobio sam slijedeći ispis: Hello there, and what's your name? Chris Your name is Chris? What a lovely name! Pleased to meet you, Chris. :)
Hmmm,...izgleda Hmmm,...izgleda da, kad bih upisao, C, h, r, i, s, i pritisnuo Enter, -gets- bi zgrabio sva slova koja sam -17-
napisao, pa čak i Enter. Na sreću, postoji metoda kojia se brine za ovakve pojave – chomp-. Ova metoda izostavlja Enter koji se unese na kraju stringa, odnosno uklanja posljednji znak u stringu, ukoliko se radi o tzv. separatoru. Hajde da pokušamo preurediti malo prethodni program, sada sa upotrebom metode – chomp- : puts name puts puts
'Hello 'Hello there, and what\'s your name?' name?' = gets.chomp 'Your 'Your name is ' + name + '? '? What a lovely name!' name!' 'Pleased 'Pleased to meet you, ' + name + '. '. :)' :)'
Ispis: Hello there, and what's your name? Chris Your name is Chris? What a lovely name! Pleased to meet you, Chris. :)
Tako! To je već bolje! Primjetit ćete da varijabla „ name” ukazuje na vezanu metodu gets.chomp, tako da samoj varijabli ne moramo pridruživati metodu ( name.chomp).
Par stvarčica za oprobati Napišite program koji traži od osobe da napiše svoje ime, srednje ime, a onda i prezime. Program bi nakon toga trebao pozdraviti osobu punim imenom i prezimenom. •
• •
Napišite program koji pita osobu za omiljeni broj. Neka onda vaš program uveća taj broj za jedan i onda ispiše rezultat predloži osobi kao novi omiljeni broj.
Kad završite s ovim programima, onda možemo krenuti dalje, upoznati se s novim metodama. gets- i tako dalje (Nagradno Imali smo, do ovog momenta, priliku vidjeti nekoliko metoda, - puts- i – gets pitanje: možete li pobrojati sve metode s kojima smo se do sada upoznali? Ima ih deset; a odgovor koje su – slijedi.), ali nismo puno govorili o tome šta su zapravo metode. Znamo šta one rade, ali ne znam još uvijek šta one zapravo jesu. Ustvari to i jeste njihova suština – da rade koješta u programima koje pišemo. Ako u Ruby kao jeziku objekte (kao što su strings, integers, floats) predstavimo kao imenice, onda su metode vrlo slične glagolima. Baš kao i u jeziku, ne možete imati glagol, a da nemate određenu imenicu koja će ga izvršiti (radnju). Na primjer, „kucanje“ nije nešto što se jednostavno dešava; moraćemo imati sat koji to radi. U jeziku bismo mogli reći da „sat kuca“. Kad bi tu situaciju trebalo prevesti u Ruby, onda bismo naveli u kodu clock.tick (pretpostavljajući da je clock objekat u Ruby). Programeri bi najvjerovatnije najvjerovatnije rekli da su pozvali metodu tick, objekta clock ili . . . „nad objektom clock smo pozvali metodu tick“. Jeste li odgovorili na nagradno pitanje? Dobro. Siguran sam da ste se sjetili metoda – puts-, -gets- i – chomp-, iz razloga što smo ih nedavno naučili i ponovili mnogo puta. Garant ste se sjetili i metoda za pretvorbu - to_i-, -to_s-, -to_f -. -. Međutim, jeste li uspjeli dokučiti koje su preostale četiri? Ma haj'te, pa to nije ništa drugo nego naši dobri stari drugovi [ +], [-], [*] i [/] – matematički operatori. Kao što sam već rekao, svaki glagol treba svoju imenicu, pa tako i svaki objekt treba neku metodu. Obično je vrlo lahko reći koji objekat izvršava metodu; to je onaj koji stoji ispred tačke, kao u našem primjeru clock.tick , ili na primjer 101. to_s. Ponekad, međutim, nije sve tako očito; kao što je slučaj sa aritmetičkim metodama. Kako to i izgleda, napisati 5 + 5 je samo kraći način od pisanja 5.+ 5; na primjer -18-
puts 'hello 'hello '.+ 'world 'world' ' puts (10 (10.* .* 9).+ 9
Ispis: hello world 99
Ovako postaviti aritmetičke metode nije baš najljepše, tako da ih mi nikad nećemo pisati u ovom obliku; važno je, međutim, razumjeti šta se ustvari događa. (Na mom računaru, došlo mi je i upozorenje „ parenthesize argument(s) for future versions “ – „argumente ubuduće stavi u zagrade“! Iako je pokrenuo moj program bez problema, računar me je ipak upozorio da ne zna šta sam želio reći ovim kodom i tražio je od mene da u budućnosti manje koristim zagrade.) Ovaj nam primjer takođe objašnjava zašto možemo pomnožiti žabu peticom, ali ne možemo peticu pomnožiti žabom. 'žaba' * 5, govori 'žabi' da učini množenje, a 5 * 'žaba' govori petici da to učini. 'žaba' zna kako da napravi pet svojih kopija i da ih doda jedne drugima; međutim, petica će ipak imati daleko više problema praveći 'žaba' svojih kopija i onda ih dodati jedne drugima.
gets- tiče. Gdje su njihovi objekti (ispred)? U Naravno, ostaje nam štošta objasniti što se metoda – puts- i – gets jeziku koji govorimo, kao što i sami znate, ponekad možemo izostaviti imenicu; na primjer ako VILLAIN govori „Umri!“, imenica na koju se ukazuje na onoga na koga se on dere. U Ruby, ako ja kažem - puts 'to be or not to be ' – to ustvari znači – self.puts 'to be or not to be '.– Šta je pa sad self? Self je specijalna varijabla/promjenjiva koja pokazuje na onaj objekat u kome se nalazite. Još uvijek mi ne znamo šta znači biti u objektu, ali dok ne spoznamo, treba reći da ćemo uvijek biti u jednom velikom objektu koji je... sam program koji smo napisali je objekat! Sreća naša, sam program ima svoje metode, kao što su – puts- i – gets-. Vidi ovo: iCantBelieveIMadeAVariableNa iCantBelieveIMadeAVariableNameThisLongJust meThisLongJustToPointToA3 ToPointToA3 = 3 puts iCantBelieveIMadeAVariableNa iCantBelieveIMadeAVariableNameThisLongJust meThisLongJustToPointToA3 ToPointToA3 self.puts iCantBelieveIMadeAVariableNa iCantBelieveIMadeAVariableNameThisLongJust meThisLongJustToPointToA3 ToPointToA3
Ispis: 3 3 Ako niste upratili baš sve kako treba, nema veze. Važno iz svega zaključiti da je svaka metoda izvršena od strane nekog objekta, čak i ako nema tačke ispred objekta. Ukoliko ste shvatili, onda smo spremni da idemo dalje.
Zanimljive metode namijenjene znakovima (string) Haj'te da naučimo par zanimljivih zanimljivih metoda koje tretiraju znakove (stringove). Ne morate ih sve učiti napamet; jednostavno pogledajte ovu stranicu ponovo ako ih zaboravite. Namjera mi je pokazati vam samo jedan dio onoga što možete učiniti sa znakovima (stringovima). Da budem iskren, ni ja sam ne mogu upamtiti polovinu metoda koje tretiraju string – ali nema veze, jer postoje izvrsne reference na internetu, izlistane i sa detaljnim objašnjenjima o njihovoj primjeni. (Uputit ću vas na linkove na kojima ćete naći liste metoda.) Uistinu, ja i ne želim da znam sve te metode; to nekako dođe kao znati sve riječi iz riječnika. Mogu govoriti engleski solidno i bez poznavanja sve do jedne riječi koja je napisana u riječniku...uostalom to je poenta riječnika...zar ne? Ono što mi u njemu ne znamo je njegova poenta, inače nam ne bi trebao. -19-
Dakle, prva metoda na koju ćemo obratiti pažnju je – reverse -, metoda kojia će nam dati obrnuti redoslijed znakova u nekom stringu. var1 = 'stop 'stop' ' var2 = 'stressed 'stressed' ' var3 = 'Can 'Can you pronounce this sentence backwards?' backwards?' puts puts puts puts puts puts
var1.reverse var2.reverse var3.reverse var1 var2 var3
Ispis: pots desserts ?sdrawkcab ecnetnes siht ecnuonorp uoy naC stop stressed Can you pronounce this sentence backwards?
Kao što i sami možete vidjeti, - reverse - ne „izvrće” izvorni string, nego pravi novu „rikverc” verziju istog. Iz tog razloga var1 ostaje 'stop' čak i nakon što smo pozvali metodu – reverse- nad njom. U nizu metoda je i lenght -, metoda – lenght -, koja nam govori koliko znakova (uključujući i razmake, prazne prostore) ima određeni string: puts 'What 'What is your full name?' name?' name = gets.chomp puts 'Did 'Did you know there are ' + name.length + ' characters in your name, ' + name + '? '?'
Ispis: What is your full name? Christopher David Pine #
Uh! Uh! Nešto je izgleda krenulo po zlu, a desilo se nakon linije name = gets.chomp... Možete li uočiti gdje u čemu je problem? Pokušajte otkriti? Problem je nastao sa metodom – length length- : ona kao rezultat daje broj, a ono što mi želimo je string. Ništa lakše, jednostavno ćemo na čitav izraz nabaciti metodu - to_s( i prekrstit' prste [/] držat' fige ): puts name puts your
'What 'What is your full name?' name?' = gets.chomp 'Did 'Did you know there are ' + name.length.to_s + ' characters in name, ' + name + '? '?' -20-
Ispis: What is your full name? Christopher David Pine Did you know there are 22 characters in your name, Christopher David Pine?
Ma nemoj mi reći,…toliko slova u mom imenu,…nisam znao. Napomena: broj koji je program ispisao je ukupan broj
znakova u ovom stringu ( name), a ne broj slova koje moje ime sadrži (eto prebroj'te ih). Mogli bismo napisati i program koji pita ime, srednje ime, prezime (svako posebno) i onda sabira broj slova svakog od njih...hej! pa zašto to nebiste sami napravili! Hajde, šta čekate!? Jeste li? Dobro! Fin programčić, zar ne? Nakon par poglavlja poslije poslije ovoga, bićete i sami začuđeni onim što ćete znati da programirate. Postoji takođe i par metoda koje mogu mijenjati kapitalizaciju slova (malo/veliko slovo) koja se nalaze u nekom od vaših stringova. Metoda – upcase - pretvoriće sva mala slova u velika, a metoda – downcase- sva velika u mala slova. Nadalje, metoda – swapcase swapcase- pravi inverziju stringa, jer slova koja su velika pretvara u mala, a ona koja su mala pretvara u velika . . . i konačno – capitalize- je metoda slična metodi – downcase -, s razlikom da na početak stringa stavlja veliko slovo (ako su slova u pitanju). letters = 'aAbBcCdDeE 'aAbBcCdDeE' ' puts letters.upcase puts letters.downcase letters.downcase puts letters.swapcase letters.swapcase puts letters.capitalize letters.capitalize puts ' a'.capitalize puts letters
Ispis: AABBCCDDEE aabbccddee AaBbCcDdEe Aabbccddee a aAbBcCdDeE
To je već standarno, očekivano. Primetićete da u liniji „ puts ' a'.capitalize “, metoda – capitalize capitalize- samo pretvara u veliko slovo početni znak, ne prvo slovo [ k.p. : koje je u ovo slučaju razmak ]. Takođe, kao što sam to i ranije napominjao, uprkos pozivanju raznih metoda, izvorna slova ostaju nepromijenjena. Nemam namjeru biti dosadan s ponavljanjem, ali je važno da to shvatite. Postoje metode koje mijenjaju objekt koji ih izvršava, ali mi nismo još došli do njih,...i nećemo tako brzo. Poslijednja od ovih zanimljivih metoda odnosi se na uređivanje izgleda ispisa. Prva od njih, – center -, -, dodaje razmake na početku i kraju stringa da bi ga postavila u sredinu - centrirala. Međutim, baš onako kako morate reći metodi – puts- šta želite ispisati, ili metodi [ +] šta želite dodati/sabrati, tako i metodi – center- morate reći kolika širina tog razmaka treba da bude. Dakle, ako bih htio centrirati stihove iz jedne pjesme, učinio bih slijedeće: -21-
lineWidth = 50 puts( 'Old 'Old Mother Hubbard'.center(lineWidth)) Hubbard'.center(lineWidth)) puts( 'Sat 'Sat in her cupboard'.center(lineWidth)) cupboard'.center(lineWidth)) puts( 'Eating 'Eating her curds an whey,'.center(lineWidth)) whey,'.center(lineWidth)) puts( 'When 'When along came a spider'.center(lineWidth)) spider'.center(lineWidth)) puts( 'Which 'Which sat down beside her'.center(lineWidth)) her'.center(lineWidth)) puts('And puts('And scared her poor shoe dog away.'.center(lineWidth)) away.'.center(lineWidth))
Ispis: Old Mother Hubbard Sat in her cupboard Eating her curds an whey, When along came a spider Which sat down beside her And scared her poor shoe dog away.
Hmmm,…nisam siguran koliko je ova rima pogodna, ali mi je lijeno sad voditi računa o tome. (Htio sam takođe izravnati i dio linije .center lineWidth, te sam zbog toga stavio razmake ispred stringa. Mislio sam da će tako ljepše izgledati. Programeri su vrlo često kritični kad je u pitanju šta je „lijepo na oko“ u programu i često ćete naići na različite stavove. stavove. Što više uđete u programiranje, sve više ćete razvijati neki svoj stil.) Kad smo kod lijenosti, u programiranju ona nije uvijek tako loša. Vidite li, na primjer, kako sam ja smjestio dužinu linije u varijablu lineWidth? Kad bih kasnije htio učiniti linije širim, jedino bih morao mijenjati prvu liniju u programu tj. varijablu, a ne svaku vrijednost koja radi centriranje. Ako bi ovo bio neki veći program, onda bi mi ovaj potez uštedio dosta vremena. Ova vrsta lijenosti u programiranju je zaista vrlina. Kako vam se čini centriranje...primjetićete da nije baš onako lijepo kako to izgleda u nekim uređivačima teksta. Ako je savršeno centriranje ono što želite (i možda malo ljepši font), onda biste jednostavno trebali koristiti neki uređivač teksta! Ruby je sjajan alat, ali nijedan alat nije pravi alat za baš sve poslove. Preostale metodu su nam – ljust -, koje pozicioniraju tekst na lijevu ili desnu stranu razvlačeći ga ljust - i – rjust rjust -, cijelom širinom linije. Slični su metodi – center -, -, osim što pomiču string razmacima nalijevo, odnosno nadesno, respektivno. Hajde da ih vidimo na djelu: lineWidth = 40 str = '--> '--> text <--' <--' puts str.ljust lineWidth puts str.center lineWidth puts str.rjust lineWidth puts str.ljust (lineWidth/2 (lineWidth/2) + str.rjust (lineWidth/2 (lineWidth/2)
Ispis: --> text <---> text <---> text <---> text <-- --> text <--
-22-
Par stvarčica za oprobati Napišite program koji će se zvati „ Ljuti šef “. “. Trebao bi vas pitati šta želite. Šta god da mu odgovorite, „Ljuti šef“ bi trebao vikati na vas i onda vas otpustiti. Na primjer, ukoliko napišete „Želim da mi povećate platu.“, on bi vikao: „Želim da mi povećate platu!? To želiš!? E otkaz je ono što ćeš dobiti!“ •
Evo jednog zadatka koji će omogućiti da se malo poigrate metodama za formatiranje teksta (- center-, -ljust- i – rjust rjust-). Napišite program koji će napraviti sadržaj ovog tutoriala kao što je prikazano u ispisu: •
Table of Contents Chapter 1: Numbers page Chapter 2: Letters page Chapter 3: Variables page
1 72 118
Viša matematika ( Naredni dio ovog poglavlja je potpuno neobavezan. Pretpostavlja se za one koji ga žele čitati da imaju solidno znanje iz matematike. Ukoliko niste zainteresovani, možete odmah krenuti dalje, na „ Kontrola toka“, bez ikakvih problema. Međutim, bilo bi korisno da makar bacite pogled na dio koji se odnosi na generisanje brojeva metodom slučajnog uzorka, moglo bi vam nekad zatrebati. ) Postoji približno isti broj metoda za brojeve, kao i za string objekte (iako ih ja ne znam sve napamet, kao i prethodne). U ovom kratkom dijelu ćemo sagledati ostatak aritmetičkih metoda, vidjeti kako izgleda generator slučajnih brojeva, Math objekat, sa svojim trigonometrijskim i transcendentalnim metodama.
Više aritmetike Dvije metode s kojima ćemo započeti ovaj dio su [ ** ] (eksponencija) i [ %] (modulus – ostatak nakon dijeljenja). Dakle, ako želite reći Ruby „pet na kvadrat“, onda biste trebali napisati izraz 5 ** 2. U proračunima možete takođe koristiti i float objekte za opis eksponenta na koji podižete brojeve, pa ćete u tom slučaju, ako želite naći kvadratni korijen od pet napisati 5 ** 0.5. Metoda za modulus daje vam rezultat ostatak nakon dijeljenja dva broja. Na primjer, ako dijelim 7 sa 3, onda kao rezultat dobijem 2 i ostatak 1. Da vidimo kako to izgleda u programu: puts puts puts puts puts
5** **2 2 5** **0.5 0.5 7/3 7%3 365% 365 %7
Ispis: 25 2.23606797749979 2 1 1
Iz poslijednje linije smo naučili da godina ima prilično veliki broj sedmica, plus jedan dan. Ako je vaš rođendan bio u utorak ove, slijedeće godine bi trebao pasti u srijedu. Sa metodom [%] mogu se takođe koristiti i float objekti. U suštini, radi na jedini mogući osjetljivi način za njega,...ali taj ću dio ostaviti vama, da se malo igrate. -23-
Pomenut ću samo još jednu metodu prije nego pređemo na generator slučajnog broja: - abs-. Ta metoda jednostavno računa apsolutnu vrijednost broja: puts((5 puts((5-2).abs) puts((2 puts((2-5).abs)
Ispis: 3 3
Slučajni brojevi Ruby ima prilično zgodan generator slučajnog broja (random). Metoda koja pomaže da se dobije neki slučajni broj je – rand -. Ako pozovete tu metodu bez specifikacije, onda ćete dobiti slučajni float koji će rand -. vrijednosno biti smješten ili jednak od 0.0 do 1.0. Međutim, ako metodi – rand - pridružite neki integer (recimo 5), daće vam kao rezultat integer veći ili jednak nuli, a manji od petice (dakle, pet mogućih brojeva, od 0 do 4). Pogledajmo Pogledajmo sada – rand rand - u akciji. [k.p. Ukoliko na originalnom tutorialu učitate ovu stranicu ponovo, brojevi će se promijeniti svaki put kad ih učitate.] (...pa znali ste da sam sve ove programe, osim što sam ih napisao, pokrenuo/izvršavao pokrenuo/izvršavao na računaru, zar ne?) puts rand puts rand puts rand puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(1 puts(rand(1)) puts(rand(1 puts(rand(1)) puts(rand(1 puts(rand(1)) puts(rand(99999999999999 puts(rand(9999999999999999999999999999 9999999999999999999999999999 9999999999999999999999999999 99999999999999999 999)) )) puts('The puts('The weatherman said there is a '+rand(101 '+rand(101).to_s+' ).to_s+'% % chance of rain,') rain, ') puts('but puts('but you can never trust a weatherman. weatherman.') ')
Ispis: 0.593474785354615 0.660763674309727 0.422511416882603 78 27 38 0 0 0 9150617696349645162059916535 91506176963496 4516205991653541403904700431 41403904700431576692785375935 57669278537593542 42 The weatherman said there is a 100% chance of rain, but you can never trust a weatherman. -24-
Zamijetit ćete da sam koristio rand (101) (101) da bih dobio za rezultat brojeve između 0 i 100; ali i da će za rand(1) rezultat uvijek biti 0. Nerazumijevanje vrijednosti koje bi se mogle očekivati kao rezultat je jedna od najvećih grješaka koje ljudi prave sa metodom – rand -, -, pa čak i profesionalni programeri; programeri; na nekim proizvodima koji su plasirani na tržište mogu se uočiti takve grješke. Ima sam čak jednom neki CD player, pa kad bih stavio muziku na „ random play “ modus, mogao sam preslušati sve pjesme osim posljednje... (pitam se šta bi se desilo da sam imao CD na kome se nalazila samo jedna pjesma?) Ponekad će biti potrebno da – rand - ispiše iste slučajne brojeve istim redoslijedom na sva različita izvršenja programa. (Na primjer, jednom sam korisito slučajne brojeve da generišem slučajno/spontano slučajno/spontano mapu za računarsku igru. Ako bih naišao na mapu koja bi mi odgovarala, htio bih je igrati ponovo, ili bih je mogao poslati prijatelju.) Da bismo postigli navedeno, treba postaviti SEED, što se može učiniti deriviranom metodom – srand -. -. 'vako to se radi: srand 1776 puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts '' srand 1776 puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) )) puts(rand(100 puts(rand(100)) ))
Ispis: 24 35 36 58 70 24 35 36 58 70
Svaki put kad ga SEEDATE istim brojem, program će dati isti rezultat. Ukoliko želite dobiti drukčije brojeve (što bi se desilo da uopšte nismo koristili – srand -) onda jednostavno jednostavno pozovite srand 0. Ovakav srand -) SEED koristi stvarno čudan broj, koji između ostalo može dati i trenutno vrijeme na vašim računarima, do u milisekundu.
„Math“ objekat Konačno, da vidimo šta je taj „Math“ objekat. Možda je najbolje da odmah uskočim u primjer:
-25-
puts( Math::PI) puts( Math::E) puts( Math.cos( Math::PI/3 ::PI/3)) puts( Math.tan( Math::PI/4 ::PI/4)) puts( Math.log( Math::E**2 ::E**2)) puts((1 + Math.sqrt(5 .sqrt(5))/2 ))/2)
Ispis: 3.14159265358979 2.71828182845905 0.5 1.0 2.0 1.61803398874989
Prva stvar koju ćete vjerovatno primjetiti je [::] – dupla dvotačka. Objasniti „ scope operator ” (što je upravo njegov opis) stvarno je izvan, hm…domašaja ovog tutoriala. Nemam namjeru vrijeđati. Kunem se. Ne treba posebno naglašavati da Math::PI daje upravo onaj rezultat koji smo i očekivali. Kao što vidite, Math objekat radi one stvari koje bismo očekivali od nekog dobrog naučnog kalkulatora/digitrona. Kao i uvijek, float je uglavnom rezultat operacija koje tretira ovaj objekat. Hajde sada, da „tečemo“!
6. Kontrola toka Aaa, kontrola toka. Ovdje „sve dolazi na svoje“. Iako je ovo poglavlje nešto kraće i lakše od prethodnog, poglavlja o metodama, vidjet ćemo da će otvoriti čitav svijet mogućnosti u programiranju. Nakon ovog poglavlja, bićete zaista u stanju napisati interaktivan program; programi koje smo pisali prije ovog poglavlja „govorili“ različite stvari na osnovu onoga što smo unijeli putem tastature/tipkovnice, ali nakon ovog poglavlja ćete biti u stanju napisati i one programe koji će „uraditi“ različite stvari. No, prije nego dođemo do toga, moraćemo naučiti da pravimo razliku među objektima u našim programima. Trebaće nam...
Metode poređenja Hajde da „protutnjimo“ kroz ovaj podnaslov, kako bi smo mogli što prije preći na „ Grananje“, podnaslov gdje ćemo naći sve ono što je zanimljivo. Dakle, da bi utvrdili da li jedan objekat veći ili manji od drugog, koristimo metode [ >] i [<], kao u slijedećem primjeru: puts 1 > 2 puts 1 < 2 -26-
Ispis: false true
Kako vidite, tu nije bilo nikakvih problema. Slično prethodnom primjeru, saznaćemo da li je neki objekat veći ili jednak, odnosno manji ili jednak drugom koristeći metode [ >=] i [<=] puts 5 >= 5 puts 5 <= 4
Ispis: true false
Konačno, želimo li saznati da li su dva objekta jednaka ili ne, koristeći metode [ ==] (što znači „jesu li ovi objekti jednaki?”) jednaki?”) i [ !=] (što znači „jesu li ovi objekti različiti?”). Da ne bi došlo do zabune, važno je napomenuti da metoda [ =] služi za dodijeljivanje vrijednosti varijabli, a metoda [ ==] za prethodno navedenu svrhu. Ove dvije metode se ne smiju izjednačiti. puts 1 == 1 puts 2 != 1
Ispis: true true
Moguće je, naravno, uporediti i string objekte (tekst). Kriterij za poređenje znakova je njihov leksikografski red, što praktično znači njihov red u riječniku – u abecedi. Imenica (u engleskom jeziku) 'cat' dolazi prije imenice 'dog' i u riječniku, pa tako će biti i u ovom primjeru: puts 'cat 'cat' ' < 'dog 'dog' '
Ispis: true
Postoji jedna mala caka: način na koji računari obično funkcionišu jer da poredaju prvo velika slova, pa tek onda mala. (Tako se redaju znakovi i u fontovima [font fajlovima]: prvo dolaze sva velika slova, a onda mala.) To bi značilo da riječ 'Zoo' dolazi prije 'ant', pa ako želite upitati koja riječ dođe prije u riječniku, pobrinite se da koristite ili samo mala, ili samo velika slova u riječi,...ili da ih „provučete“ kroz metodu -capitalize- [k.p.: sjećate li se metoda koje se bave kapitalizacijom?]. Poslijednja napomena prije nego pređemo na „ Grananje“: Rezultati koje nam daju metode poređenja ' true' [tačno] i 'false' [netačno] nisu string tip objekta, nisu obični tekst,...to su specijalni objekti true i false. [k.p.: -27-
logički tip podatka, nazvan još i Boolean.] (Naravno, ako bi smo upotrijebili true. to_s, onda bi rezultat bio 'istinski' istinit, jer bi nam - puts- dao string kao rezultat. 'true' i 'false' redovno se pojavljuju u svim programima kod kojih nailazimo na...
Grananje Grananje je jako jednostavan, ali jako moćan koncept u programiranju. Ustvari, tako je jednostavan da se kladim da ga uopšte ne moram posebno objašnjavati; jednostavno ću vam pokazati na primjeru: puts 'Hello, 'Hello, what\'s your name?' name?' name = gets.chomp puts 'Hello, 'Hello, ' + name + '. '.' if name == 'Chris 'Chris' ' puts 'What 'What a lovely name!' name!' end
Ispis: Hello, what's your name? Chris Hello, Chris. What a lovely name!
Ali, ako upišemo neko drugo ime…
Ispis: Hello, what's your name? Chewbacca Hello, Chewbacca.
To bi bilo grananje. Ako je tačno [ true] ono što dolazi poslije uslova [ if ], ], onda će biti izvršen kod između [if ] i [end], a ako poslije uslova odgovor bude netačan [ false] onda taj dio koda neće biti izvršen. Jednostavno. Uvrstio sam kod između [ if ] i [end] jer sam mislio da će tako biti lakše pratiti grananje. Skoro svi programeri to rade, bez obzira koji programski jezik koriste. Možda vam se sada čini da to i nije od velike pomoći, međutim, kad se stvari zakomplikuju, vidjet ćete kako je velika razlika. Često se desi da bismo željeli da program učini jedno, ako je uslov zadovoljen [ true] ili drugo ako nije [false]. Za tu svrhu imamo [ else]: puts 'I 'I am a fortune-telle fortune-teller. r. Tell me your name:' name:' name = gets.chomp if name == 'Chris 'Chris' ' puts 'I 'I see great things in your future. future.' ' else puts 'Your 'Your future is... Oh my! Look at the time!' time!' puts 'I 'I really have to go, sorry!' sorry!' end -28-
Ispis: I am a fortune-teller fortune-teller. . Tell me your name: Chris I see great things in your future.
Ako pokušamo s drukčijim imenom...
Ispis: I am a fortune-teller fortune-teller. . Tell me your name: Ringo Your future is... Oh my! Look at the time! I really have to go, sorry!
Grananje je neka vrsta odabira puta u kodu: dali da krenemo putanjom onih kod kojih je name == 'Chris' , ili da odaberemo neku drugu putanju [ else]. Baš kao što postoje grančice da grani jednog drveta, tako i grane u programiranju imaju grane koje se nalaze unutar njih samih: puts 'Hello, 'Hello, and welcome to 7th grade English.' English.' puts 'My 'My name is Mrs. Gabbard. And your name is...?' is...?' name = gets.chomp if name == name.capitalize name.capitalize puts 'Please 'Please take a seat, ' + name + '. '.' else puts name + '? '? You mean ' + name.capitalize + ', ', right?' right?' puts 'Don\'t 'Don\'t you even know how to spell your name??' name??' reply = gets.chomp if reply.downcase == 'yes 'yes' ' puts 'Hmmph! 'Hmmph! Well, sit down!' down!' else puts 'GET 'GET OUT!!!' OUT!!!' end end
Ispis: Hello, and welcome to 7th grade English. My name is Mrs. Gabbard. And your name is...? chris chris? You mean Chris, right? Don't you even know how to spell your name?? yes Hmmph! Well, sit down!
Dobro, ako ispravim ono u čemu sam pogriješio... pogriješio...
Ispis: -29-
Hello, and welcome to 7th grade English. My name is Mrs. Gabbard. And your name is...? Chris Please take a seat, Chris.
Ponekad će možda biti teško razaznati na koje mjesto postaviti sve te if , else, end. Ja sebi olakšam tako što odmah napišem [ if ], ], a ispod njega [ end]. Dakle kad sam pisao pređašnji program, isprva je izgledao ovako:
puts 'Hello, 'Hello, and welcome to 7th grade English.' English.' puts 'My 'My name is Mrs. Gabbard. And your name is...?' is...?' name = gets.chomp if name == name.capitalize name.capitalize else end
Nakon toga sam dodao komentare, dijelove dijelove koda u programu koje će računar ignorisati prilikom izvršavanja izvršavanja programa: puts 'Hello, 'Hello, and welcome to 7th grade English.' English.' puts 'My 'My name is Mrs. Gabbard. And your name is...?' is...?' name = gets.chomp if name == name.capitalize name.capitalize # Gospođa je u ovom slučaju pristojna. else # ...a u ovom izgubi živce. end
Sve što dođe poslije znaka # smatra se komentarom (osim, naravno, ako taj znak ne pišete unutar stringa). Nakon toga sam komentare zamijenio pravim kodom. Neki vole ostaviti komentare, ali ja, kako napredujem u Ruby, sve manje ih koristim. Da budem iskren, smatram da većinom odvlače pažnju pažnju od pravog koda. Stvar je ličnog izbora, hoćete li koristiti komantare,...sami ćete pronaći svoj stil. Onda, moj slijedeći korak u pisanju ovog koda bio je: puts 'Hello, 'Hello, and welcome to 7th grade English.' English.' puts 'My 'My name is Mrs. Gabbard. And your name is...?' is...?' name = gets.chomp if name == name.capitalize name.capitalize puts 'Please 'Please take a seat, ' + name + '. '.' else puts name + '? '? You mean ' + name.capitalize + ', ', right?' right?' puts 'Don\'t 'Don\'t you even know how to spell your name??' name??' reply = gets.chomp if reply.downcase == 'yes 'yes' ' else -30-
end end
Nanovo sam napisao [ if ], ], [else] i [end] odmah, u isto vrijeme. To mi stvarno pomaže da se bolje snalazim u kodu, jer se mogu fokusirati na manje dijelove koda ispunjavajući dio koji se nalazi između [ if ] i [end]. Druga pogodnost ovog pristupa je što računar može razumjeti program u svakoj njegovoj fazi. Svaku od verzija programa, iako nedovršenu, računar će moći da izvrši – neće biti grješaka. Dakle, još uvijek nedovršeni, ali programi koji ipak funkcionišu. Na taj način sam mogao ujedno i testirati program, dok ga pišem, što mi je pomoglo da izađem na kraj sa stvarima na koja je još trebalo poraditi. Kad je testiranje bilo završeno, onda sam znao da je posao dobro odrađen. Slijedi nekoliko caka koje će vam pomoći s grananjem, ali i nekim drugim tipovima kontrole toka:
Kruženje Često će biti potrebno narediti računaru da ponovi neki postupak nekoliko puta, što je nešto u čemu bi računari trebali da su stvari dobri. Kad kažete računaru da ponavlja nešto, morate se pobrinuti i da mu kažete kad da stane. Njima nikad ne dosadi da to rade, tako da, ako im ne kažete da stanu, oni neće prestati. Da bismo se pobrinuli da se ovo ne desi, naređujemo računaru da ponavalja određenu metodu sve dok se ispunjava određeni uslov, dok je on [true] ili jednak zadanoj vrijednosti. Ovako bi to otprilike izgledalo: command = '' while command != 'bye 'bye' ' puts command command = gets.chomp end puts 'Come 'Come again soon!' soon!'
Ispis: Hello? Hello? Hi! Hi! Very nice to meet you. Very nice to meet you. Oh... how sweet! Oh... how sweet! bye Come again soon!
To bi, dakle, bilo kruženje. (Vjerovatno ste primjetili prazan string na početku ispisa; rezultat je to koji daje puts-, prije prvog - gets-. Kako biste preuredili kod, pa da se riješimo prve linije. Pokušajte! Nije li prvi – puts rezultat bio isti kao i kod originalnog programa (iznad), samo što nije sadržavao prazne linije u ispisu?) Kružne petlje, siguran sam da i sami možete zamisliti, dozvoliće da se uradi dosta zanimljivih stvari. Međutim, mogu prouzrokovati prouzrokovati dosta problema, napravite li grješku u kodu. Šta ako se desi da grješka u -31-
kodu uzrokuje beskonačno ponavljanje? Ako mislite da se ovo desilo, pokušajte kombinaciju tipki CTRL + C . Prije nego se nastavimo igrati sa kružnim petljama, hajde da naučimo par stvarčica koje će nam olakšati posao.
Malo logike... Hajde da pogledamo prvi program u “Grananju” ponovo. Šta da je moja žena došla kući, vudjela program, isprobala ga i nije joj rekao kako divno ime ima? Ja baš i ne bih htio povrijediti njena osjećanja (ili spavati tu noć na kauču), pa sam kod ispravio na slijedeće: puts 'Hello, 'Hello, what\'s your name?' name?' name = gets.chomp puts 'Hello, 'Hello, ' + name + '. '.' if name == 'Chris 'Chris' ' puts 'What 'What a lovely name!' name!' else if name == 'Katy 'Katy' ' puts 'What 'What a lovely name!' name!' end end
Ispis: Hello, what's your name? Katy Hello, Katy. What a lovely name!
Izgleda da radi, bez problema,…ali nije baš najljepši program. Zašto ne? Pa, najveće pravilo koje sam naučio u programiranju je NP pravilo: Nemoj ponavljati. Vjerovatno bi se dala knjiga napisati o tome zašto je to jako dobro pravilo. U našem slučaju, linija puts 'What a lovely name!' je ona koja se ponavalja. Zašto je to tako strašno? Šta das sam napravio grješku u pisanju, kad sam ponovo pisao istu liniju koda? Šta ako sam želio ‘lovely’ prepraviti u neki drugi atribut, u oba primjera/linije? Sjećate li se da sam rekao da sam lijen? U suštini, htio sam da program kaže kako su imena lijepa samo kad putem metode – get- dobije unos 'Chris' i 'Katy'...ukratko, samo u slučaju da dobije jedno od ta dva imena, trebalo bi uraditi istu stvar: puts 'Hello, 'Hello, what\'s your name?' name?' name = gets.chomp puts 'Hello, 'Hello, ' + name + '. '.' if (name == 'Chris 'Chris' ' or name == 'Katy 'Katy') ') puts 'What 'What a lovely name!' name!' end
Ispis: Hello, what's your name? Katy Hello, Katy. What a lovely name!
-32-
Tako je već mnogo bolje. Da bih ostvario ono što sam naveo, koristio sam logički operator [ or] (ili). Ostali logički operatori koje ćemo koristiti su [ and] (i) i [not] (ne). Uvijek je dobar potez koristiti zagrade kada radimo sa ovim operatorima. operatorima. Pogledajmo kako oni rade: iAmChris = true iAmPurple = false iLikeFood = true iEatRocks = false puts puts puts puts puts puts puts puts puts puts puts puts
(iAmChris and iLikeFood) (iLikeFood and iEatRocks) (iAmPurple and iLikeFood) (iAmPurple and iEatRocks) (iAmChris or iLikeFood) (iLikeFood or iEatRocks) (iAmPurple or iLikeFood) (iAmPurple or iEatRocks) (not iAmPurple) (not iAmChris )
Ispis: true false false false true true true false true false
Samo jedan od ovih primjera bi vas mogao malo zbuniti. U govoru, dok s nekim pričamo, koristeći „ili“ želimo reći da od dvije opcije biramo samo jednu, ali ne oboje. Naprimjer, majka vas može upitati šta želite za dezert, pitu ili kolač. Time nije rekla da možete dobiti oboje! Računar, s druge strane, koristi „ili“ [or] kao logički operator koji znači da ili jedna ili druga vrijednost može odgovarati uslovu, pa da se on ispuni. (Još jedan način da se kaže „ako je makar jedan od ovih uslova tačan [true].“) Zato računari i jesu zanimljiviji od majki.
Par stvarčica za oprobati „99 bottles of beer on the wall... “ Napišite program koji će ispisati tekst ove stare, dobre pjesme. Napišite program pod imenom „ Gluha baba“. Šta god da joj kažete (otipkate), ona će odgovoriti sa velikim HA?! MALO GLASNIJE!, osim ako joj tekst unesete velikim slovima. Ukoliko vičete, onda će vas i čuti (ili ( ili bar tako ona misli) i odgovoriti vam NISAM! NISAM, SINKO JOŠ OD 1938! Da bi vaš program bio zaista uvjerljiv, neka umjesto „sinko“ svaki put izgovori neko drugo ime; a možda da tome i dodate da umjesto 1938 navede neku slučajno generisanu godinu (random) između 1938 i 1950 (Ovaj dio je opcionalan, a biće dobro da pročitate poglavlje o metodama, da se malo • •
-33-
podsjetite o generisanju slučajnih brojeva u Ruby). Na kraju, nećete se moći otarasiti babe, dok joj ne kažete ĆAO, BABA! Caka #1: nemojte zaboraviti metodu – chomp chomp-, jer „ĆAO, BABA!“ sa Enterom i bez njega, nije isto! Caka #2: pokušajte otkriti koji dio programa se treba ponavljati; taj dio bi trebao biti uvršen u kružnu petlju koja počinje sa [ while]. Proširite svoj program „Gluha baba“: Šta ako baba ne želi da idete? Kad joj viknete ĆAO, BABA!, ona će se praviti da vas ne čuje. Promijenite pređašnji program tako da joj tri puta morate reći ĆAO, BABA! zaredom. Pobrinite se da dobro da testirate testirate vaš program: ukoliko joj ne kažete ĆAO, BABA! tri puta zaredom, ona će i dalje htjeti da razgovara s vama. „Protekle prijestupne godine“. Napišite program koji će pitati početnu i krajnju godinu, te na osnovu toga izračunati koliko prijestupnih godina ima između tog roka, uključujući i njih same. Prijestupne su godine dijeljive sa 4 (kao 1984 ili 2004). Međutim, godine dijeljive sa 100 nisu prijestupne osim ako su u isto vrijeme dijeljive i sa 400 (kao 1600 i 2000, koje su bile prijestupne). (Da, prilično je zapetljano.) •
•
Kad završite s programima, napravite malu pauzu! Stvarno smo već dosta toga naučili. Čestitke! Jeste li iznenađeni koliko postoji stvari koje možete narediti računaru da uradi? Par poglavlja dalje i bićete u stanju programirati sve što poželite. Ozbiljno! Pogledajte samo koliko stvari možete sad kad ste naučili grananje i kruženje, a ranije bez toga niste mogli. Na redu su nam nove vrste objekata, oni koje bilježe liste nekih drugih objekata: Nizovi .
7. Nizovi i upravljači Hajde da napišemo program koji traži od nas da upišemo onoliko riječi koliko želimo (jednu riječ po liniji, sve dok ne pritisnemo Enter u praznoj liniji), a nakon toga ponavlja te riječi po abecednom redu. OK? Pa...prvo ćemo,...hm...ovaj... ćemo,...hm...ovaj... moglo bi se...mmm... Znate šta, mislim da to ne možemo napraviti. Treba nam nešto što će pohraniti nepoznat nepoznat broj riječi i držati ih sve na broju, da se ne bi pomiješale sa drugim varijablama/promjenjivim. Trebaće nam dakle nizovi. Najjednostavnije rečeno, nizovi predstavljaju liste na vašem računaru. Svako mjesto u nizu ponaša se kao varijabla: možete vidjeti na kojem mjestu se nalazi određeni objekat i možete takođe ukazati i na neki drugi objekat unutar tog niza. Da pogledamo kako izgledaju neki nizovi: [] [5] ['Hello [' Hello', ', 'Goodbye 'Goodbye'] '] flavor = 'vanilla 'vanilla' ' # Ovo, naravno, nije niz… [89.9, flavor, [true, false]] # ...ali ovo jeste. -34-
Dakle, prvo imamo prazan niz, onda niz koji sadrži samo jedan broj, nakon toga niz koji sadrži dvije riječi (string). Poslije toga dolazi jednostavno dodijeljivanje varijable; onda niz koji sadrži tri objekta, od kojih je poslijednji i sam niz [true, false]. Upamtite, varijable nisu objekti, tako da naš poslijednji niz sadrži slijedeće objekte: float, string i niz [ array]. Čak i ako bismo podesili 'flavour' da ukazuje na nešto drugo, to ne bi promijenilo niz. Da bismo mogli naći određeni objekt u nizu, sva popunjena mjesta u niz su brojčano indeksirana. Programeri (kao i većina matematičara) počinju brojati brojati od nule, tako da prvo mjesto (slot) u kome je smješten neki objekat ima 0 u indeksu. Evo kako bismo uspostavili vezu brojčanog indeksa i objekata u nizu: names = ['Ada ['Ada', ', 'Belle 'Belle', ', 'Chris 'Chris'] '] puts puts puts puts puts
names names[0 names[0] names[1 names[1] names[2 names[2] names[3 names[3]
# Ovaj indeks je nepostojeći.
Ispis: Ada Belle Chris Ada Belle Chris nil
puts names- ispisuje svako od imena u nizu names. Nakon toga koristimo – Dakle, možemo vidjeti da – puts puts names[0]- da bismo dobili ispis objekta koji je na prvom mjestu u nizu, - puts names[1]- za ispis slijedećeg,... Siguran sam da bi s početka moglo zvučati zbunjujuće, ali navikne se čovjek, brzo. Jednostavno se morate prilagoditi mišljenju da brojanje počinje od nule, a prestati misliti da riječ prvi odgovara tom broju. Na ruci postoji pet prstiju, a njihovi su brojevi 0,1,2,3 i 4. Na kraju, shvatit ćete kako su indeksirani objekti u nizu kad počnete za redati objekt od „nultog“. Da , nulti je prava riječ; postoji; pitajte bilo kojeg programera ili matematičara. Konačno, pokušali smo ispisati – puts names[3]-, samo da bi vidjeli šta se desi. Očekivali ste grješku? Ponekad, kad postavite postavite neko pitanje, a ono nema smisla (bar ne za računar) onda u ispisu dobijete dobijete grješku. S druge strane, ponekad možete postaviti pitanje a da njegov odgovor bude „ništa“ [ nil]. Šta je na mjestu koje nosi indeks [3]? Ništa. „ Nil “ je način na koji nam Ruby poručuje da se na indeksu [3] našeg niza ne nalazi ništa,...a „ nil “ se tretira kao objekat koji znači nepostojanje bilo kakvog objekta. Ako vas plaši svo ovo nabrajanje, koje mjesto koji objekat zauzima u nizovima, ne bojte se! Često ih možemo izbjeći koristeći neke od metoda karakteristične za nizove, kao naprimjer:
Metoda „each“ -each- nam dozvoljava da učinimo nešto (bilo šta da želimo) sa svakim pojedinim bojektom sadržanom u nekom nizu. Tako dakle, ako želimo reći nešto lijepo o svakom od jezika u nizu, onda ćemo napisati slijedeći slijedeći kod: -35-
languages = ['English ['English', ', 'German 'German', ', 'Ruby 'Ruby'] '] languages.each do |lang| puts 'I 'I love ' + lang + '! '!' puts 'Don\'t 'Don\'t you?' you?' end puts 'And 'And let\'s hear it for C++!' C++!' puts '... '...' '
Ispis: I love English! Don't you? I love German! Don't you? I love Ruby! Don't you? And let's hear it for C++! ...
Šta se upravo desilo? Uspjeli smo proći kroz sve objekte u nizu, a nismo morali da nabrajamo svaki pojedinačno, koristeći indeksirane brojeve za poziciju u nizu. Prevedeno na jezik početnika, pređašnji program razumijeva to kao naredbu: „za svaki objekat u nizu languages napravi varijablu lang koja će ukazati na objekat unutar niza i onda učini sve što ti kažem dok ne dođeš do kraja niza, odnosno kraja metode“. (Jedna mala opaska: među brojnim programskim jezicima, postoji i jedan zvani C++, kojega je daleko teže naučiti od Ruby; obično C++ program ima mnogo više linija nego bi imao neki program sa istom svrhom napisan u Ruby.) Možda se zapitate, „Ova metoda je slična kružnoj petlji [loop], koji smo naučili u poglavlju prije ovoga.“ Da, slična jeste. Međutim, jedna vrlo važna razlika je da je – each- samo to: metoda; dok while i end nisu metode (kao što to nisu do, if , else). Ove rezervisane riječi su dijelovi Ruby kao programskog jezika, baš kao što su to znakovi =, ili zagrade; ne neki način kao znakovi interpunkcije u jeziku. Ali ne i – each each-; -each- je metoda namijenjena nizovima. Metode koje služe kao kruženje (kružne petlje), kao što je –each-, često se nazivaju upravljači ili ITERATORI . Jedna fina stvar kod ITERATORA je da su uvijek praćeni riječima do . . . end. Druge, poput while i if , nikad nisu trebale do u svojoj bilizini; koristimo ih samo kod ITERATORA. Slijedi još jedan mali primjer ITERATORA, ali ne kao metode namijenjene nizu nego kao ...integer metode! 3.times do puts 'Hip-Hip-Hooray! 'Hip-Hip-Hooray!' ' end
Ispis: Hip-Hip-Hooray! Hip-Hip-Hooray! -36-
Hip-Hip-Hooray!
Još nekoliko metoda namijenjenih nizovima Dakle, naučili smo metodu – each-, međutim, ima još dosta njih koje su namijenjene nizovima, gotovo jednak broj koliko ih postoji i za string objekte! Ustvari, neke od njih (kao što su – length-, -reverse-, -+- i -*-) rade isto onako kako bi radile i sa stringovima, osim što rade na pozicijama objekata u stringu, a ne na znakovima koje sadrži string. Druge metode, kao što su – last- i – join- specifične su samo za nizove. Ima i onih kao što su – push push- i -pop-, koje zapravo mogu izmijeniti niz. Ono što sam ranije rekao za pamćenje metoda za stringove, važi i za metode koje su posvećene nizovima; ne morate ih pamtiti sve dok vam je poznato gdje ćete ih pronaći (ovdje). Prvo ćemo pogledati -. to_s- i – join- metode. – join- metoda radi slično metodi -. to_s- , a razlika je što dodaje string između dva objekta u nizu. Da pogledamo: foods = ['artichoke ['artichoke', ', 'brioche 'brioche', ', 'caramel 'caramel'] '] puts puts puts puts puts puts puts
foods foods.to_s foods.join(', foods.join(', ') foods.join(' :) ') + ' 8) 8)' '
200.times do 200.times puts [] end
Ispis: artichoke brioche caramel artichokebriochecaramel artichoke, brioche, caramel artichoke :) brioche :) caramel 8)
Kao što možete vidjeti, - puts- tretira nizove različito od ostalih objekata: jednostavno poziva put string za svaki od objekata u nizu. Iz tog razlova „putsovanje“ praznog niza 200 puta neće dati nikakav rezultat; niz ne ukazuje na bilo kakve objekte, pa tako ni za metodu – puts- nema ničega što bi ispisala. (Uraditi ništa 200 puta jednačko je ničemu.) Pokušajte „putsovati“ neki niz koji sadrži nizove kao objekte; da li je rezultat ono što ste očekivali? Takođe, da li ste primjetili da sam ispred – puts- stavio prazan string svaki put kad bih htio dobiti novi prazan red u ispisu? Da sada pređemo na metode – push-, -pop- i – last last-. Metode – push push- i – pop pop- su opozitne, slično opoziciji push- dodaje objekat na kraj niza, a metoda – pop- uklanja objekat sa koju predstavljaju – i +. Metoda – push pop- utoliko poslijednjeg poslijednjeg mjesta u nizu (usput govoreći koji je to objekat). Metoda – last- slična je metodi – pop -37-
što govori koji se objekat nalazi na poslijednjem mjestu u nizu, ali ne vrši nad njim nikakvu operaciju. Što push- i -pop- ustvari vrše izmjene na nizu: je važno, metode – push favorites = [] favorites.push 'raindrops 'raindrops on roses' roses' favorites.push 'whiskey 'whiskey on kittens' kittens' puts favorites[0 favorites[0] puts favorites.last puts favorites.length favorites.length puts favorites.pop puts favorites puts favorites.length favorites.length
Ispis: raindrops on roses whiskey on kittens 2 whiskey on kittens raindrops on roses 1
Par stvarčica za oprobati Napišite program o kome smo pričali na početku. (Caka: postoji jedna divna metoda za sortiranje objekata u nizu: -sort- Iskoristit I skoristitee ga!) Pokušajte isti program napisati bez korištenja metode –sort-. Dobar dio programiranja je ustvari riješavanje riješavanje problema, dajte se onda u vježbu! Napišite ponovo program „SADRŽAJ“ (iz poglavlja o metodama). metodama). Počnite program s nizom koji će sadržavati sve naslove koje sadržaj treba da ima (ime poglavlja, broj strane,...). Onda ispišite informacije informacije iz niza u divno formatiran „SADRŽAJ“. •
•
•
Mnogo je metoda koje smo do sada naučili. Sad je vrijeme da naučimo i sami praviti metode. Naše vlastite.
8. Pisanje vlastitih metoda Kako smo mogli vidjeti, kružne petlje i upravljači (iteratori) dozvoljavaju nam da određene dijelove koda, odnosno programa, ponavaljamo željeni broj puta. Međutim, ponekad će biti potrebno da uradimo istu stvar nekoliko puta, ali na različitim različitim mjestima unutar programa. Naprimjer: recimo da pišemo upitnik za studenta psihologije. Prema onome što sam dobio od studenata psihologije, upitnik bi vjerovatno izgledao ovako: puts puts puts puts puts puts
'Hello, 'Hello, and thank you for taking the time to to' ' 'help 'help me with this experiment. My experiment' experiment' 'has 'has to do with the way people feel about' about' 'Mexican 'Mexican food. Just think about Mexican food' food' 'and 'and try to answer every question honestly,' honestly,' 'with 'with either a "yes" or a "no". My experiment' experiment' -38-
puts 'has 'has nothing to do with bed-wetting.' bed-wetting.' puts # Postavljamo pitanja, ali zanemarujemo odgovore na njih. goodAnswer = false while (not goodAnswer) puts 'Do 'Do you like eating tacos?' tacos?' answer = gets.chomp.downcase gets.chomp.downcase if (answer == 'yes 'yes' ' or answer == 'no 'no') ') goodAnswer = true else puts 'Please 'Please answer "yes" or "no".' "no".' end end goodAnswer = false while (not goodAnswer) puts 'Do 'Do you like eating burritos?' burritos?' answer = gets.chomp.downcase gets.chomp.downcase if (answer == 'yes 'yes' ' or answer == 'no 'no') ') goodAnswer = true else puts 'Please 'Please answer "yes" or "no".' "no".' end end # Međutim na „ovaj“ odgovor ćemo obratiti pažnju. goodAnswer = false while (not goodAnswer) puts 'Do 'Do you wet the bed?' bed?' answer = gets.chomp.downcase gets.chomp.downcase if (answer == 'yes 'yes' ' or answer == 'no 'no') ') goodAnswer = true if answer == 'yes 'yes' ' wetsBed = true else wetsBed = false end else puts 'Please 'Please answer "yes" or "no".' "no".' end end goodAnswer = false while (not goodAnswer) puts 'Do 'Do you like eating chimichangas?' chimichangas?' answer = gets.chomp.downcase gets.chomp.downcase if (answer == 'yes 'yes' ' or answer == 'no 'no') ') goodAnswer = true else puts 'Please 'Please answer "yes" or "no".' "no".' end end -39-
puts 'Just 'Just a few more questions...' questions...' goodAnswer = false while (not goodAnswer) puts 'Do 'Do you like eating sopapillas?' sopapillas?' answer = gets.chomp.downcase gets.chomp.downcase if (answer == 'yes 'yes' ' or answer == 'no 'no') ') goodAnswer = true else puts 'Please 'Please answer "yes" or "no".' "no".' end end # Postavite još neka pitanja, ako želite, na isti način. puts puts puts puts puts puts puts puts puts puts puts
'DEBRIEFING: 'DEBRIEFING:' ' 'Thank 'Thank you for taking the time to help with' with' 'this 'this experiment. In fact, this experiment experiment' ' 'has 'has nothing to do with Mexican food. It is is' ' 'an 'an experiment about bed-wetting. The Mexican' Mexican' 'food 'food was just there to catch you off guard' guard' 'in 'in the hopes that you would answer more' more' 'honestly. 'honestly. Thanks again.' again.' wetsBed
Ispis: Hello, and thank you for taking the time to help me with this experiment. My experiment has to do with the way people feel about Mexican food. Just think about Mexican food and try to answer every question honestly, with either a "yes" or a "no". My experiment has nothing to do with bed-wetting. Do you like eating tacos? yes Do you like eating burritos? yes Do you wet the bed? no way! Please answer "yes" or "no". Do you wet the bed? NO Do you like eating chimichangas? yes -40-
Just a few more questions... Do you like eating sopapillas? yes DEBRIEFING: Thank you for taking the time to help with this experiment. In fact, this experiment has nothing to do with Mexican food. It is an experiment about bed-wetting. The Mexican food was just there to catch you off guard in the hopes that you would answer more honestly. Thanks again. false
Bijaše to prilično dug program, sa puno ponavljanja. (Svi dijelovi koji se tiču hrane su bili identični, samo se dio oko mokrenja u krevet malo razlikovao.) Ponavljanje nije baš dobra stvar. U nekoj većoj petlji se ne bismo mogli baš najbolje snaći jer ponekad ima stvari koje želimo uraditi između određenih pitanja. U ovakvim situacijama, najbolje je napisati vlastitu metodu. Evo i kako: def sayMoo puts 'mooooooo... 'mooooooo...' ' end
Uh...program nije mukao kao krava. Zašto ne? Nismo mu rekli da to uradi. Hajde da pokušamo ponovo: def sayMoo puts 'mooooooo... 'mooooooo...' ' end sayMoo sayMoo puts 'coin-coin 'coin-coin' ' sayMoo sayMoo
Ispis: mooooooo... mooooooo... coin-coin mooooooo... mooooooo...
Ah, to je već puno bolje. (U slučaju da ne govorite francuski, to je u sredini patka. Na francuskom se patke oglašavaju sa „ coin-coin “.) Tako, definisali smo metodu sayMoo. (Imena metoda, kao i imena varijabli, počinju malim slovom. Postoje, ipak, iznimke kao što su + ili ==.) Zar ne trebaju metode uvijek biti povezane s nekim objektima? gets-) metoda je povezana sa objektom Naravno da trebaju,...u ovom slučaju (kao što je to i sa – puts- i – gets -41-
koji predstavlja čitav program. U slijedećem poglavlju ćemo vidjeti kako dodati metode drugim objektima. Ali, prije nego nastavimo, samo ćemo nakratko pogledati šta su...
Parametri metoda Primjetili ste da neke od metoda (kao - gets-, -.to_s-, -reverse-...) možete jednostavno jednostavno pozvati pridružujući ih objektu. Međutim, drugim metodama (kao što su –[ +]-, -[-]-, -puts-) su potrebni parametri koji govore objektima kako da izvrše određenu metodu. Naprimjer, ne možete napisati samo 5+, zar ne? Govorite petici da doda/sabere, ali ne govorite šta da doda. Parametri se dodaju metodama na slijedeći način: def sayMoo numberOfMoos puts 'mooooooo... 'mooooooo...'*numberOfMoos '*numberOfMoos end sayMoo 3 puts 'oink-oink 'oink-oink' ' sayMoo # Ovdje bi se trebala pojaviti grješka, jer je parametar prazan.
Ispis: mooooooo...mooooooo...mooooooo... oink-oink # numberOfMoos varijabla je koja predstavlja parametar naše metode. Reći ću to još jednom, numberofMoos je varijabla koja predstavlja parametar naše metode. Dakle, ako unesem u kod „ sayMoo 3“, onda je parametar moje metode 3, a numberOfMoos ukazuje na tricu.
Kao što možete vidjeti, parametar je u ovom slučaju obavezan/neophodan. Kako će vaš jadni računar znati da treba ponavaljati „ mooo“ ako mu ne podesite parametar za takvu akciju. Ako je objekat za Ruby ono što je imenica za jezik, metoda glagol, onda bi parametar bio jednak prilogu (priloška odredba) (u našem primjeru bi nam parametar trebao otprilike reći kako da izvrši „sayMoo“) ili ponekad kao direktnom objektu (kao – puts puts-, gdje se parametar ispisuje zbog komande koju – puts- daje).
Lokalne varijable U slijedećem programu, postoje dvije varijable...: def doubleThis num numTimes2 = num*2 num*2 puts num.to_s+' doubled is '+numTimes2.to_s end doubleThis 44
Ispis: 44 doubled is 88 -42-
…te varijable su num i numTimes2 . Obje su smještene unutar doubleThis metode. Ove dvije (kao i sve varijable koje smo imali priliku vidjeti do sada) nazivaju se lokalne varijable. To znači da one „žive“/vrijede samo unutar određene metode i da izvan nje ne mogu biti primjenjivane...pokušamo li, javit će se grješka: def doubleThis num numTimes2 = num*2 num*2 puts num.to_s+' doubled is '+numTimes2.to_s end doubleThis 44 puts numTimes2.to_s
Ispis: 44 doubled is 88 #>
Nedefinisana lokalna varijabla,...mi jesmo definisali tu lokalnu varijablu, ali ona nije lokalna tamo gdje smo je pokušali koristiti, nego je lokalna za metodu. Možda se čini nepovoljnim, ali je to ustvari dobra stvar. To znači da nema pristupa varijablama koje se nalaze unutar metoda, ali i da metode ne mogu pristupiti varijablama,...gdje postoji mogućnost da ih te metode izmjene: def littlePest var var = nil puts 'HAHA! 'HAHA! I ruined your variable!' variable!' end var = 'You 'You can\'t even touch my variable!' variable!' littlePest var puts var
Ispis: HAHA! I ruined your variable! You can't even touch my variable!
U ovom malom programu, postoje dvije varijable imena var : jedna unutar littlePest metode, a druga izvan nje. Kad pozovemo metodu littlePest na izvršenje, nismo ništa drugo uradili nego prebacili string iz jedne varijable u drugu, tako da obje ukazuju na isti string. Nakon toga littlePest ukazuje svoju lokalnu varijablu na nil (ništa), ali to nije promijenilo vanjsku varijablu var .
Povratne vrijednosti Možda ste primjetili da neke metode daju povratnu vrijednost kad ih pozovemo. Naprimjer, integrisana gets- vraća string koji smo unijeli putem tastature/tipkovnice, a metoda –[ +]- u 5+3 vraća metoda – gets vrijednost 8. Aritmetičke metode za brojeve vraćaju brojeve, a aritmetičke metode za string vraćaju isti. -43-
Važno je da razumijete razliku između metoda koje imaju povratne vrijednosti – daju rezultat - od metoda koje po pozivu ispisuju informacije na ekranu, kao što to radi metoda – puts-. Obratite pažnju,...5+3 vraća vrijednost 8, ne ispisuje informaciju 8. Šta onda metoda – puts puts- vraća? Ranije nismo marili za to, ali hajde da pogledamo sada: returnVal = puts 'This 'This puts returned:' returned:' puts returnVal
Ispis: This puts returned: nil
puts- vratio nil (ništa). Iako nismo testirali i drugi je vratio istu vrijednost; - puts- uvijek Dakle, prvi je – puts vraća nil (ništa) [k.p.: primjetit ćete, ukoliko koristite irb - interactive Ruby- da će poslije - puts- biti naznačena često i linija u kojoj stoji nil ]. ]. Na kraju, svaka metoda mora nešto vratiti, pa makar to bila i nil vrijednost (ništa). Napravite malu pauzu i napišite program koji će saznati šta je povratna vrijednost metode sayMoo. Iznenađeni? Evo kako to funkcioniše: vrijednost koju metoda vraća jednaka je poslijednjoj liniji te metode. U našem slučaju, to znači – puts 'moooo...'*numberOfMos-, 'moooo...'*numberOfMos-, što vraća nil , jer je to uvijek povratna puts-. Dakle, ako bismo željeli da naše metode vraćaju string ' yellow submarine', vrijednost metode – puts jednostavno bismo je trebali staviti na kraj. def sayMoo numberOfMoos puts 'mooooooo... 'mooooooo...'*numberOfMoos '*numberOfMoos 'yellow submarine' submarine' end x = sayMoo 2 puts x
Ispis: mooooooo...mooooooo... yellow submarine
Da pokušamo onaj psihološki test, ponovo, ali ovaj put ćemo napisati metodu koja će postavljati pitanja za nas. Moraće uzeti pitanje kao parametar, a kao povratnu vrijednost daće vrijednost true ako je odgovor tačan (yes), a false ukoliko je odgovor netačan ( no). (Iako smo većinom ignorisali ignorisali odgovor, ipak je dobra ideja učiniti da naša metoda vraća neku vrijednost. Možemo je takođe lakše iskoristiti za pitanje o mokrenju kreveta.) Skratit ću uvod i pozdrav, da bi bilo lakše čitati: def ask question goodAnswer = false while (not goodAnswer) puts question reply = gets.chomp.downcase gets.chomp.downcase -44-
if (reply == 'yes 'yes' ' or reply == 'no 'no') ') goodAnswer = true if reply == 'yes 'yes' ' answer = true else answer = false end else puts 'Please 'Please answer "yes" or "no".' "no".' end end answer end
# Ovo je naša povratna vrijednost (true or false).
puts 'Hello, 'Hello, and thank you for...' for...' puts ask 'Do 'Do you like eating tacos?' tacos?' # Ovu povratnu vrijednost zanemarujemo. ask 'Do 'Do you like eating burritos?' burritos?' wetsBed = ask 'Do 'Do you wet the bed?' bed?' # Ovu čuvamo u varijabli. ask 'Do 'Do you like eating chimichangas?' chimichangas?' ask 'Do 'Do you like eating sopapillas?' sopapillas?' ask 'Do 'Do you like eating tamales?' tamales?' puts 'Just 'Just a few more questions...' questions...' ask 'Do 'Do you like drinking horchata?' horchata?' ask 'Do 'Do you like eating flautas?' flautas?' puts puts 'DEBRIEFING:' puts 'Thank you for...' puts puts wetsBed
Ispis: Hello, and thank you for... Do you like eating tacos? yes Do you like eating burritos? yes Do you wet the bed? no way! Please answer "yes" or "no". Do you wet the bed? NO Do you like eating chimichangas? yes Do you like eating sopapillas? yes Do you like eating tamales? yes Just a few more questions... Do you like drinking horchata? -45-
yes Do you like eating flautas? yes DEBRIEFING: Thank you for... false
Nije loše, ha? Bili smo u mogućnosti dodati još pitanja (a to je sada lahko), a naš program ima znatno manje linija u kodu! Za lijenog programera, to je stvarno veliko poboljšanje. poboljšanje.
Još jedan veliki primjer Mislim da bi još jedan primjer bio od pomoći da još bolje upoznate metode. Nazvat ćemo ga englishNumber . Metoda će uzeti broj od korisnika, naprimjer 22, te ga onda vratiti kao broj izgovoren na engleskom jeziku (u ovom slučaju string ' twenty-two '). Trenutno ćemo uvrstiti samo integer vrijednosti između 0 i 100. (Napomena: Ova metoda koristi novi trik da se vrati na početak, koristeći rezervisanu riječ return, a predstavlja predstavlja nam i novi izraz u grananju: elsif . Iz konteksta će se lahko izvući zaključak kako funkcionišu oba ova nova dodatka.) def englishNumber number # Želimo samo brojeve između 0 i 100. if number < 0 return 'Please 'Please enter a number zero or greater.' greater.' end if number > 100 return 'Please 'Please enter a number 100 or lesser. lesser.' ' end numString = '' # Ovo je varijabla za ispis rezultata. # "left" je koliko nam još broja ostaje za ispisati. # "write" je dio koji trenutno ispisujemo. # "napiši" i "ostalo"... kontaš? :) left = number write = left/100 left/100 # Koliko stotica ima za ispisati? left = left - write*100 write*100 # Oduzeti stotice. if write > 0 return 'one 'one hundred' hundred' end write = left/10 left/10 # Koliko desetica ima za ispisati? left = left - write*10 write*10 # Oduzeti desetice. if write > 0 -46-
if write == 1 # Uh-oh... # Pošto ne možemo napisati "tenty-two" umjesto "twelve", # moramo napraviti iznimku za ovaj slučaj. if left == 0 numString = numString + 'ten 'ten' ' elsif left == 1 numString = numString + 'eleven 'eleven' ' elsif left == 2 numString = numString + 'twelve 'twelve' ' elsif left == 3 numString = numString + 'thirteen 'thirteen' ' elsif left == 4 numString = numString + 'fourteen 'fourteen' ' elsif left == 5 numString = numString + 'fifteen 'fifteen' ' elsif left == 6 numString = numString + 'sixteen 'sixteen' ' elsif left == 7 numString = numString + 'seventeen 'seventeen' ' elsif left == 8 numString = numString + 'eighteen 'eighteen' ' elsif left == 9 numString = numString + 'nineteen 'nineteen' ' end # Pošto smo se pobrinuli za za cifre u jedinicama # nije nam ostalo ništa za ispisati. left = 0 elsif write == 2 numString = numString + 'twenty 'twenty' ' elsif write == 3 numString = numString + 'thirty 'thirty' ' elsif write == 4 numString = numString + 'forty 'forty' ' elsif write == 5 numString = numString + 'fifty 'fifty' ' elsif write == 6 numString = numString + 'sixty 'sixty' ' elsif write == 7 numString = numString + 'seventy 'seventy' ' elsif write == 8 numString = numString + 'eighty 'eighty' ' elsif write == 9 numString = numString + 'ninety 'ninety' ' end if left > 0 numString = numString + ''-' end end write = left # Koliko jedinica ima za ispisati? left = 0 # Oduzeti jedinice. if write > 0 -47-
if write == 1 numString = numString + 'one 'one' ' elsif write == 2 numString = numString + 'two 'two' ' elsif write == 3 numString = numString + 'three 'three' ' elsif write == 4 numString = numString + 'four 'four' ' elsif write == 5 numString = numString + 'five 'five' ' elsif write == 6 numString = numString + 'six 'six' ' elsif write == 7 numString = numString + 'seven 'seven' ' elsif write == 8 numString = numString + 'eight 'eight' ' elsif write == 9 numString = numString + 'nine 'nine' ' end end if numString == '' # Jedino kad je "number" jednak nuli "numString" # može biti prazan string – odnosno nula - 'zero' return 'zero 'zero' ' end # Do ovog momenta bi trebalo da smo otkrili naš broj # pa ostaje još samo da ga ispišemo, odnosno vratimo "numString". numString end puts puts puts puts puts puts puts puts puts
englishNumber( 0) englishNumber( 9) englishNumber( 10 10) ) englishNumber( 11 11) ) englishNumber( 17 17) ) englishNumber( 32 32) ) englishNumber( 88 88) ) englishNumber( 99 99) ) englishNumber(100) englishNumber(100 )
Ispis: zero nine ten eleven seventeen thirty-two eighty-eight ninety-nine one hundred
[k.p.]: INTERVENCIJA INTERVENCIJA PREVODIOCA -48-
---------------E pa, u ovom je programu nekoliko stvari koji mi se nikako ne sviđaju, Prvo, previše je ponavljanja. Drugo, ne radi sa brojevima preko 100. Treće, previše je zasebnih slučajeva, previše return . Hajde da, koristeći nizove, pokušamo počistiti sve ove nedostatke: def englishNumber number if number < 0 # Ne uključujemo negativne brojeve. return 'Please 'Please enter a number that isn\'t negative.' negative.' end if number == 0 return 'zero 'zero' ' end # Nema više posebnih slučajeva! Nema return! numString = '' # Ovo je string koji ćemo imati za rezultat. onesPlace = ['one ['one', ', 'two 'two', ', 'three 'three', ', 'four 'four', ', 'five 'five', ', 'six 'six', ', 'seven 'seven', ', 'eight eight', ', 'nine 'nine'] '] tensPlace = ['ten ['ten', ', 'twenty 'twenty', ', 'thirty 'thirty', ', 'forty 'forty', ', 'fifty 'fifty', ', 'sixty 'sixty', ', 'seventy seventy', ', 'eighty 'eighty', ', 'ninety 'ninety'] '] teenagers = ['eleven ['eleven', ', 'twelve 'twelve', ', 'thirteen 'thirteen', ', 'fourteen 'fourteen', ', 'fifteen 'fifteen', ', 'sixteen sixteen', ', 'seventeen 'seventeen', ', 'eighteen 'eighteen', ', 'nineteen 'nineteen'] '] # "left" je koliko nam još broja ostaje za ispisati. # "write" je dio koji trenutno ispisujemo. # "napiši" i "ostalo"... kontaš? :) left = number write = left/100 left/100 # Koliko stotica ima za ispisati? left = left - write*100 write*100 # Oduzeti stotice. if write > 0 # Evo sad jedan mali trik : hundreds = englishNumber write numString = numString + hundreds + ' hundred hundred' ' # # # # # # # # # # #
To se zove "rekurzija". Šta sam upravo uradio. Rekao sam ovoj metodi da pozove sama sebe ali s parametrom "write" umjesto "number". Upamtite da je "write" trenutno broj stotica koje treba ispisati. Nakon što dodamo stotice promjenjivoj "numString", a nakon toga dodajemo ' hundred'. Tako, na primjer ako smo pozvali englishNumber sa parametrom 1999 ("number" = 1999) u tom momentu bi "write" bio 19 a "left" bi bio 99. [k.p.]: devetnaest stotina i ostatak 99. Najlijenja stvar koji možemo uraditi sad je da naredimo metodi englishNumber da ispiše 'nineteen' i samo mu doda ' hundred', a onda ostatak metode ispiše 'ninety-nine'. -49-
if left > 0 # Da ne bismo napisali 'two hundredfifty-o hundredfifty-one'... ne'... numString = numString + ' ' end end write = left/10 # Koliko desetica ima za ispisati? left = left - write*10 # Oduzeti desetice.
if write > 0 if ((write == 1) and (left > 0)) # Pošto ne možemo napisati "tenty-two" umjesto "twelve", # moramo napraviti iznimku za ovaj slučaj. numString = numString + teenagers[leftteenagers[left-1] # Faktor "-1" postavljen je iz razloga što je [3] jednak 'fourteen', a ne 'thirteen'. # Pošto smo se pobrinuli za za cifre u jedinicama # nije nam ostalo ništa za ispisati. left = 0 else numString = numString + tensPlace[writetensPlace[write-1 1] # Faktor "-1" stavljamo jer je tensPlace[3] jednak 'forty', a ne 'thirty'. end if left > 0 # Da ne bismo pisali 'sixtyfour' (spojeno)... (spojeno)... numString = numString + '-' end end write = left # Koliko jedinica ima za ispisati? left = 0 # Oduzeti jedinice. if write > 0 numString = numString + onesPlace[write-1] # Faktor "-1" postavljamo jer je onesPlace[3] jednak 'four', a ne 'three'. end # Sad jednostavno ispišemo/vratimo ispišemo/vratimo "numString"... numString end -50-
puts puts puts puts puts puts puts puts puts puts puts puts puts puts
englishNumber( 0) englishNumber( 9) englishNumber( 10) englishNumber( 11) englishNumber( 17) englishNumber( 32) englishNumber( 88) englishNumber( 99) englishNumber(100) englishNumber(100) englishNumber(101) englishNumber(101) englishNumber(234) englishNumber(234) englishNumber(3211) englishNumber(3211) englishNumber(999999) englishNumber(999999) englishNumber(1000000000000) englishNumber(1000000000000)
Ispis: zero nine ten eleven seventeen thirty-two eighty-eight ninety-nine one hundred one hundred one two hundred thirty-four thirty-two hundred eleven ninety-nine hundred ninety-nine hundred ninety-nine one hundred hundred hundred hundred hundred hundred
Ahhhh.... Tako je već mnogo bolje. Program je malo gušći, pa sam iz tog razloga napisao dosta komentara. Radi čak i sa velikim brojevima...iako ne baš nabolje, ne onako kako sam se nadao. Na primjer, 'one trilion' (milijarda) bi ljepše izgledalo, nego vrijednost koju vidimo na kraju ispisa,...ili 'one milion milion' (iako su sve tri vrijednosti tačne). Ustvari, mogli biste to upravo vi i uraditi...
Par stvarčica za oprobati Proširite program englishNumber. Prvo, ubacite hiljadinke. hiljadinke. Na taj način će vam program pisati jedna hiljada umjesto deset stotina ili deset hiljada umjesto jedna stotina stotina. Nakon ovog, napravite još jedno proširenje. Dodajte milionite dijelove, tako da se u ispisu pojavi milion umjesto hiljada hiljaada. Onda idite sve dalje i dalje u proširivanju opsega. Šta mislite, koliko daleko možete ići? Kako bi bilo da napišete program weddingNumber ? Trebao bi raditi slično kao i englishNumber, osim što bi trebalo dodati riječ ' and ' posvuda, vraćajući vrijednosti kao što je 'nineteen hundred and seventy and two' ili na neki drugi način opisati kako trebaju izgledati pozivnice za vjenčanje. Dao bih vam još primjera, ali ni sam baš najbolje ne razumijem kako to ide. Možda biste trebali kakvog organizatora vjenčanja da vam pomogne? „ Ninety-nine bottles of beer... “ sjećate li se tog programa? Koristeći princip prisutan u englishNumber, ispišite tekst ove pjesme na pravi način, ovaj put. Kaznite svoj računar: neka počne od 9999 (Mada, nemojte odabrati preveliki broj, jer,...pisanje svih tih flaša na ekranu vašeg računara moglo bi potrajati. Sto hiljada flaša bi stvarno potrajalo; a ako li biste odabrali milion, onda biste kaznili ne samo računar, nego i sebe.) •
•
•
•
-51-
Čestitam! Od ovog momenta, možete se pohvaliti da ste programeri! Naučili ste sve što je potrebno da se napiše jedan program „od nule“. Ako trenutno imate ideja koje biste mogli iskoristiti za pisanje programa,...učinite programa,...učinite to odmah! Pretvorite svoje ideje u funkcionalne programe, pokušajte! Naravno, pisanje programa „od nule“ može nekad i da potraje. Zašto gubiti vrijeme i pisati kod koji je neko već napisao? Hoćete li da vaš program šalje e-mail? Ili želite pohraniti ili učitati fajlova sa vašeg računara? Kako vam se čini generisanje web stranica za tutorial koje stvarno izvršavaju kod, svaki put kad ih učitate? Ruby ima veliki broj različitih različitih objekata koji nam mogu pomoći da napišemo bolje programe za što manje vremena. Zato...idemo dalje...
9. Klase Do ovog momenta, vidjeli smo različite vrste, ili klase, objekata: string, integer, float, nizove i par specijalnih objekata ( true, false , nil ) o kojima ćemo nešto kasnije. Klase u Ruby uvijek počinju velikim slovom: String, Integer, Float, Array,... Kad želimo kreirati novi objekat određene klase, onda koristimo new: a = Array.new + [12345 [12345] ] # Dodavanje niza. b = String.new + 'hello 'hello' ' # Dodavanje stringa. c = Time.new puts 'a 'a = '+a.to_s puts 'b 'b = '+b.to_s puts 'c 'c = '+c.to_s
Ispis: a = 12345 b = hello c = Sat Jan 31 07:18:32 GMT 2009
Iz razloga što nizove i znakove možemo kreirati pomoću [...] i '...' (respektiv ( respektivno), no), rijetko koristimo new . (Iako to možda nije očigledno iz pređašnjeg primjera, za kreiranje praznog stringa koristimo String.new , a za prazan niz koristimo Array.new) Treba napomenuti da su brojevi izuzetak/iznimka: ne možete kreirati integer koristeći Integer.new , nego jednostavno taj integer napišete.
Klasa za vrijeme – „Time“ Klasa za vrijeme? O čemu se tu radi? Objekat Time predstavlja određeni vremenski momenat. Na ovaj objekat možemo dodati ili oduzeti određene (brojčane) vrijednosti, vrijednosti, da bismo dobili „novo“ vrijeme, odnosno neki drugi vremenski momenat: ako na trenutno vrijeme dodamo 1.5, onda ćemo kao rezultat dobiti vrijeme 1.5 sekundu kasnije: time = Time.new # Momenat koji ste dobili u trenutku ispisa. time2 = time + 60 # Minuta kasnije. puts time puts time2
Ispis: -52-
Sat Jan 31 07:18:32 GMT 2009 Sat Jan 31 07:19:32 GMT 2009
Ovaj objekat možete koristiti i da biste kreirali određen vremenski momenat,...koristeći Time.mktime: puts Time.mktime(2000 Time.mktime(2000, , 1, 1) # Y2K. puts Time.mktime(1976 Time.mktime(1976, , 8, 3, 10 10, , 11 11) ) # Datum mog rođenja.
Ispis: Sat Jan 01 00:00:00 GMT 2000 Tue Aug 03 10:11:00 GMT 1976
Napomena: datum mog rođenja sadrži format Pacific Daylight Savings Time (PDT). Kada je nastupila 2000.-ta godina, vrijeme je dobilo Pacific Standard Time (PST) format, bar za nas sa zapadne obale. Zagrade služe da grupišu parametre mktime. Što više parametara dodate, tačnije vrijeme ćete dobiti. Možete koristiti poredbene metode da uporedite određena vremena (manja cifra je starije), te ako ih oduzmete jedno od drugog, dobijete broj sekundi između njih. Pokušajte, Pokušajte, igrajte se malo!
Par stvarčica za oprobati ...napišite program koji će reći prije koliko sekundi ste rođeni (ako umijete). Milijarda sekundi ...napišite Saznajte kada ćete biti milijardu sekundi stari (ili ste to već)? Program će vam pomoći da to označite na svoj kučnom kalendaru J Sretan rođendan ! Napišite program koji će osobu pitati godinu, mjesec i dan rođenja. Otkrijte na taj način koliko je osoba stara, pa joj za svaku godinu dajte jedan...SPANK! •
•
„Hash“ klasa Još jedna korisna klasa je „ Hash“. Ne može se reći da je ovaj objekat poput niza, jer nizovi imaju mnogo slotova, indeksiranih brojevima, počevši od nule, smještenih u redove. Za „ hash“ ne možemo reći da su smješteni u redove, a u slotove se može ubaciti svaki objekat, ne samo broj [k.p.: indeksirani su i po nekim drugim vrijednostima, ne samo brojevima, kako je to slučaj kod nizova]. Dobro je koristiti „ hash“ kada imate dosta stvari koje želite imati pri ruci, a te se stvari ne mogu baš poredati u niz uređen brojčanim indeksom. Naprimjer, boje koje sam koristio za ovaj tutorial: colorArray = [] # isto kao Array.new colorHash = {} # isto kao Hash.new colorArray[0] = 'red 'red' ' colorArray[1] = 'green 'green' ' colorArray[2] = 'blue 'blue' ' colorHash['strings colorHash['strings'] '] = 'red 'red' ' colorHash['numbers colorHash['numbers'] '] = 'green 'green' ' colorHash['keywords colorHash['keywords'] '] = 'blue 'blue' ' colorArray.each colorArray.each do |color| puts color end -53-
colorHash.each do |codeType, color| puts codeType + ': ': ' + color end
Ispis: red green blue strings: red keywords: blue numbers: green
Kad koristim nizove, moram pamtiti da je slot 0 za string, slot 1 za integer, itd. Međutim, „ hash“ mi dozvoljava da znatno pojednostavim stvari! Slot ' strings strings' sadrži boju za tekst, naravno. Ništa ne moram pamtiti. Primjetit ćete da, kad smo koristili – each-, program nije ispisao objekte unutar „hash-a“ redoslijedom kojim smo ga mi unosili u programu. (Možda nisu dok sam ja ovo pisao, kod mene. Možda su neki drugi put, nikad se ne zna s „hash“ klasama.) Nizovi su ti koji sve drže u savršenom redu – „hash“ klase nisu za to zadužene. Iako ljudi većinom koriste string da bi imenovali slotove „hash“ klasa, moguće je umetnuti bilo kakav objekat, čak i nizove ili druge „hash“ klase (iako mi trenutno ne pada na pamet zašto biste radili ovo poslijednje): weirdHash = Hash.new weirdHash[12 weirdHash[12] ] = 'monkeys 'monkeys' ' weirdHash[[]] = 'emptiness 'emptiness' ' weirdHash[Time.new] weirdHash[Time.new] = 'n 'no o time like the present' present'
Hash klase i nizovi su korisni u različitim situacijama, pa je na vama da odaberete šta je bolje koristiti za određeni problem, koji sebi postavite.
Proširivanje klasa Na kraju prošlog poglavlja, napisali smo metodu koja je „čitala“ brojeve. Međutim, to nije bila metoda koju smo mogli primjeniti na neki integer, nego jednostavno – metoda koja je predstavaljala čitav program. Ne bi li bilo lijepo kad bismo mogli jednostavno napisati nešto kao 22. to_eng umjesto englishNumber 22? Evo kako bi to izgledalo: class Integer def to_eng if self == 5 english = 'five 'five' ' else english = 'fifty-eight 'fifty-eight' ' end english end -54-
end # Dobro bi bilo da testiram na par brojeva... puts 5.to_eng puts 58 58.to_eng .to_eng
Ispis: five fifty-eight
Ja sam testirao program, izgleda da radi. ;) Dakle, definisali smo metodu za integer „uskačući“ u Integer klasu, definišući metodu unutar nje. Sada će svi integeri biti podvrgnuti ovoj (pomalo nepotpunoj) metodi. Ustvari, ako vam se nije sviđalo kako metoda za stringove - to_s- radi, možete je jednostavno redefinisati na način sličan ovom primjeru...ali vam ne bi preporučio da je dirate, bilo kako bilo! Predefinisane metode programskog jezika najbolje je ostaviti onakvim kakve jesu i u slučaju da želimo nešto novo – napraviti nove, vlastite metode ili klase. Zbunjeni...? Zbunjeni...? Da prođemo još jednom kroz ovaj poslijednji poslijednji program. Sve do sada, kad god bismo izvršili kod ili definirali metode, radili smo to u programu kao objektu. U pređašnjem primjeru programa, program kao objekat smo ostavili i ušli u Integer klasu. Unutar nje smo definisali metodu (što je čini integer metodom) koju mogu koristiti svi integer objekti. Unutar te metode koristimo rezervisanu riječ – self - da bismo ukazali na objekat koji ta metoda koristi (integer).
Kreiranje klasa (classes) Pred nama su bile brojne klase različitih klasa objekata. Međutim, vrlo je lahko napraviti i onu koja u Ruby inače ne postoji. Na sreću, kreiranje nove klase je podjednako jednostavno kao i proširivanje neke postojeće. Recimo da smo htjeli napraviti kockicu za „ Ne ljuti se čovječe “ u Ruby. Ovako bi njena klasa izgledala: class Die def roll 1 + rand(6 rand(6) end end # Da sad napravimo par kockica... dice = [Die.new, Die.new] # ...a onda ih „bacimo“. dice.each do |die| puts die.roll end
Ispis: 6 1 -55-
(Ako ste preskočili dio u kome smo učili o generisanju slučajnog broja, evo male pomoći za vas,... rand(6) generiše slučajni broj u vrijednosti između 0 i 5.) To je sve! Imate vlastite objekte. Bacite kockice nekoliko puta ( refresh [F5] na originalnog stranici http://pine.fm/LearnToProgram/)) i pogledajte šta će se desiti. http://pine.fm/LearnToProgram/ Možemo mi definisati razne metode za naše objekte...ali nešto ipak nedostaje. Rad s ovim objektima podsjeća uveliko na programiranje prije upoznavanja upoznavanja s varijablama. varijablama. Pogledajte, naprimjer, naše kockice. Možemo i „baciti“ i svaki put će nam dati različite brojeve. Ali, ako bismo htjeli ostati na tom broju, onda bi morali napraviti varijablu koja bi taj broj zadržala, dakle, ukazivala na broj. Ukoliko bismo morali pratiti kockicu, ne bismo morali pratiti i brojeve. Međutim, ako bismo pokušali pohraniti broj koji smo dobili pri bacanju kockice u (lokalnu) varijablu, varijablu, on bi trajao do završetka bacanja. Treba nam, dakle, neki drugi tip varijable u koji ćemo smjestiti naš broj:
Neposredne varijable Obično, kad hoćemo govoriti o znakovima/tekstu, onda govorimo o stringu. Međutim, mogli bismo slobodno reći i da je to string objekat. Ponekad programeri nazovu instanca klase String , međutim, to je samo uglađen način (i prilično dug, ako mene pitate) da se kaže string. Instanca neke klase je jednostavno objekt koji pripada toj klasi. Tako dakle, neposredne varijable (instance) su varijable nekog objekta. Lokalne varijable, koje pripadaju nekoj metodi, traju do završetka metode. S druge strane, neposredne (instance) varijable nekog objekta traju sve dok taj objekat traje. Da bi smo razlikovali obične lokalne varijable od neposrednih, koristimo znak @ koji stavljamo ispred imena varijable: class Die def roll @numberShowing = 1 + rand(6 rand(6) end def showing @numberShowing end end die = Die.new die.roll puts die.showing puts die.showing die.roll puts die.showing puts die.showing
Ispis: 6 6 6 -56-
6
Vrlo dobro! Prilagođena metoda „roll“ baca kockice, a „showing“ nam govori koji broj kockica pokazuje. Međutim, šta ako pokušamo pogledati koje ćemo brojeve dobiti prije nego bacimo kockice (prije nego definišemo @numberShowing )? class Die def roll @numberShowing = 1 + rand(6 rand(6) end def showing @numberShowing end end # Pošto ovu kockicu neću ponovo koristiti # ne trebam je ni čuvati u varijabli. puts Die.new.showing Die.new.showing
Ispis: nil
Hmmm,...ako ništa, nije nam se javila nikakva grješka. Svejedno, nema smisla da nam se kocka „ne baci“, ili šta god da povratna vrijednost „ nil “ znači. Bilo bi lijepo kad bi se naše kockica mogla „podesiti“ tik prije initialize-: nego što je bacimo. Za tu svrhu koristimo – initialize class Die def initialize # Jednostavno ću „baciti“ kockicu, iako # bismo mogli učiniti s njom i štošta drugo # naprimjer, da stalno pokazuje broj 6. roll end def roll @numberShowing = 1 + rand(6 rand(6) end def showing @numberShowing end end puts Die.new.showing Die.new.showing -57-
Ispis: 2 Kada se kreira objekat, njegova – initialize- metoda (ako je definisana) uvijek se poziva. Naše su kocke skoro pa savršene. Jedno što im možda nedostaje je da nam metoda kaže koju stranu kocke pokazuje...zašto pokazuje...zašto ne biste napravili metodu koja upravo to pokazuje. Vratite se čitanju kad završite (i nakon što se svoj program testirali, naravno)! Ne dozvolite da neko podesi kockicu tako da može pokazati i sedmicu! Tako dakle, pokrili smo ovim poglavljem dosta zanimljivih stvari. Jeste pomalo komplikovano, tako da ću navesti još jedan primjer. Recimo da želimo napraviti jednostavnog virtualnog ljubimca, bebu zmaja. Kao i većina beba, trebala bi biti u mogućnosti da jede, spava i . . . „kaki“, što će reći da ćemo je morati hraniti, staviti u krevet i „izvoditi napolje“. Unutar bebe zmaja trebao bi postojati metod koji će pratiti glad, pospanost i digestiju, ali mi nećemo moći da vidimo sve to, isto kao što bebu ne možemo pitati „Jesi li gladna?“. Dodaćemo takođe još nekoliko zanimljivih sitnica koje će povećati interakciju sa našom bebom zmajem, a kad se rodi daćemo mu ime (šta god da unesete u tu novu metodu, ono se prenosi do – initializemetode). U redu, da pokušamo: class Dragon def initialize name @name = name @asleep = false @stuffInBelly = 10 # Pun stomak. @stuffInIntestine @stuffInIntestine = 0 # Ne mora ići „vani“. puts @name + ' is born.' born.' end def feed puts 'You 'You feed ' + @name + '. '.' @stuffInBelly = 10 passageOfTime end def walk puts 'You 'You walk ' + @name + '. '.' @stuffInIntestine @stuffInIntestine = 0 passageOfTime end def putToBed puts 'You 'You put ' + @name + ' to bed.' bed.' @asleep = true 3.times do if @asleep passageOfTime end if @asleep -58-
puts @name + ' snores, filling the room with smoke.' smoke.' end end if @asleep @asleep = false puts @name + ' wakes up slowly.' slowly.' end end def toss puts 'You 'You toss ' + @name + ' up into the air.' air.' puts 'He 'He giggles, which singes your eyebrows. eyebrows.' ' passageOfTime end def rock puts 'You 'You rock ' + @name + ' gently. gently.' ' @asleep = true puts 'He 'He briefly dozes off...' off...' passageOfTime if @asleep @asleep = false puts '...but '...but wakes when you stop.' stop.' end end private # "private" znači da metode koje su ovdje definisane jesu # metode interne za objekat (Možete nahraniti svoj zmajčeka # ali ga ne možete upitati da li je gladan.) # Metode se mogu završavati i sa "?". # Obično tako završavamo samo one metode koje # daju povratnu vrijednost true ili false kao: @stuffInBelly <= 2 end def poopy? @stuffInIntestine @stuffInIntestine >= 8 end def passageOfTime if @stuffInBelly > 0 # Pomjeri hranu iz stomaka u crijevo. @stuffInBelly = @stuffInBelly - 1 @stuffInIntestine @stuffInIntestine = @stuffInIntestine @stuffInIntestine + 1 else # Umrije nam zmajić od gladi! if @asleep @asleep = false puts 'He 'He wakes up suddenly!' suddenly!' end puts @name + ' is starving! In desperation, he ate YOU!' YOU!' -59-
exit # Ovdje se program završava. end if @stuffInIntestine >= 10 @stuffInIntestine @stuffInIntestine = 0 puts 'Whoops! 'Whoops! ' + @name + ' had an accident...' accident...' end if hungry? if @asleep @asleep = false puts 'He 'He wakes up suddenly!' suddenly!' end puts @name + '\'s '\'s stomach grumbles...' grumbles...' end if poopy? if @asleep @asleep = false puts 'He 'He wakes up suddenly!' suddenly!' end puts @name + ' does the potty dance...' dance...' end end end pet = Dragon.new 'Norbert 'Norbert' ' pet.feed pet.toss pet.walk pet.putToBed pet.rock pet.putToBed pet.putToBed pet.putToBed pet.putToBed
Ispis: Norbert is born. You feed Norbert. You toss Norbert up into the air. He giggles, which singes your eyebrows. You walk Norbert. You put Norbert to bed. Norbert snores, filling the room with smoke. Norbert snores, filling the room with smoke. Norbert snores, filling the room with smoke. Norbert wakes up slowly. You rock Norbert gently. He briefly dozes off... ...but wakes when you stop. You put Norbert to bed. He wakes up suddenly! -60-
Norbert's stomach grumbles... You put Norbert to bed. He wakes up suddenly! Norbert's stomach grumbles... You put Norbert to bed. He wakes up suddenly! Norbert's stomach grumbles... Norbert does the potty dance... You put Norbert to bed. He wakes up suddenly! Norbert is starving! In desperation, he ate YOU!
Opa! Naravno, bilo bi puno bolje da u ovom programu ima malo više interakcije, ali taj dio možete doraditi poslije. U ovom primjeru sam pokušao pokazati samo dijelovi koji se odnose na kreiranje nove klase – dragon -. U ovom smo primjeru vidjeli nekoliko primjera. Prvi primjer je: - exit- će okončati program tačno na onom mjestu na kojem se nalazi. Drugi: - private- rezervisana riječ koju smo stavili u sred definicije klase. Mogao sam je izostaviti, ali sam htio da napravim razliku među metodama koje su stvari koje možemo „učiniti“ našem zmaju, a druge se jednostavno samo događaju s njim. Mislite o ovim metodama kao o onom što je „pod haubom“: ukoliko niste automehaničari, sve što trebate znati o automobilima da biste ih vozili su pedale gasa i kočnice i volan. Programer bi mogao ove akcije nazvati public interface / vidljivo sučelje. Međutim, kada treba aktivirati okidač zračnog jastuka, za to se brinu interni sistemi u automobilu, sistemi za koje obični vozač i ne zna (niti mora da zna). Hajde da navedemo konkretan primjer kako predstaviti automobil u video igri (što je moj stvarni posao). Prvo bi trebalo razlučiti r azlučiti kako trebaju izgledati izgledati javna sučelja, odnosno, drugim riječim r iječima, a, koje bi metode igrači bili u mogućnosti pozvati nad objektima koje sačinjavaju automobil. Trebalo bi da su u mogućnosti tisnuti pedalu gasa, pedalu kočnice, ali i da budu u mogućnosti odrediti kojom jačinom će stisnuti pedalu (velika je razlika samo je dotaći i stisnuti do poda). Trebalo bi i da budu u mogućnosti okretati volan i opet, odrediti kojom brzinom i za koji ugao. Pretpostavljate da bismo mogli ići sve dalje i dalje,...na kvačilo, žmigavce, bacač granata, nitro pogon, itd ...zavisno od vrste igre koju pravimo. Međutim, u unutrašnjosti automobila trebalo bi se dašavati daleko više; ostale stvari, koje auto treba su brzina, smjer, pozicija (u najjednostavnijem). Ovi atributi bi se mijenjali pritiskom na papučicu gasa ili kočnice i okretanje volana, naravno, ali njih korisnik ne bi mogao mijenjati direktno (što bi bila dobra osnova za warp). Mogli biste takođe dodati pređeni put, ili oštećenje automobila prilikom utrkivanja...sve nevedeno bi bilo „unutrašnje“ u objektima prisutnim u automobilu. Par stvarčica za oprobati Napravite klasu OrangeTree . Trebala bi imati metodu height koja bi kao povratnu vrijednost davala visinu i metodu onYearPasses koja, kada se pozove, utvrdi starenje. Svake godine, drvo postaje sve visočije (koliko vi mislite da bi drvo narandže trebalo porasti godišnje) i nakon određenog vremena, kad prođe mnogo godina, drvo bi trebalo istruhnuti. Prvih nekoliko godina, ne bi trebalo proizvoditi voće, ali nakon određenog vremena, počinje rađati; valjda, što je starije drvo, biće više plodova svake godine,...šta god vama bude smisleno. Naravno, trebalo bi biti u stanju countTheOranges (probrojati narandže – povratna vrijednost je količina na drvetu) i pickAnOrange (ubrati narandžu,...što smanjuje @orangeCount za jednu narandžu i vraća string koji govori kako je ukusna narandža bila, ili vraća informaciju da su sve narandže ubrane). Pobrinite se i da sve narandže koje ne uberete opadnu prije nego dođe nova godina/sezona. Napravite program koji će dati malo više interakcije sa bebom zmaja. Trebali biste u mogućnosti dati komande kao što su nahrani i hodaj, tako da bi se ove metode mogle izvršiti nad zmajem. Naravno, kako unosite samo tekt (strings) program će morati da provjeri unos i pozove na osnovu •
•
-61-
unosa odgovarajuću metodu, što bi trebalo izvesti preko neke vrste dispečera metoda. Time smo došli do kraja ovog poglavlja! Ali,...čekajte malo...ne rekoh vam ništa o onim klasama koje šalju e-mail, pohranjuju ili učitavaju dokumente, o kreiranju prozora i dugmića, ili 3D svijetu ili,...bilo čemu! Ah, toliko različitih klasa postoji, koje su vam na raspolaganju, da nije moguće pokazati sve njih; pa, ni ja ne znam većinu njih! Ono što vam mogu pokazati je, gdje da pronađete njih i nešto više o njima, tako da biste mogli naučiti one koje želite koristiti u svojim programima. Prije nego vas otpremim, ima još jedna stvar u Ruby koju trebate znati, nešto što većina drugih programskih jezika nema, a bez čega se jednostavno ne može živjeti: blokovi i procedure,...
10. Blokovi i procedure Ovo su definitivno neke od najzanimljivijih stvari koje Ruby ima. Imaju njih i neki drugi programski jezici, iako ih oni nazivaju drukčijim imenima, ali oni najpopularniji ih nemaju – stvarno šteta! Šta je to tako cool i koje su to stvari cool u Ruby? Ono što je ustvari sjajno je da možemo odvojiti dio koda, jedan blok ( block ), ), umotati ga u jedan drugi objekat (nazvan Proc), smjestiti ga u varijablu ili predati nekoj metodi i izvršiti taj dio kad poželimo (čak i više puta, ako poželimo). Dakle, liči pomalo na metodu, osim što nije vezano za objekat (ono ustvari jeste objekat) i može se pohraniti ili predati nekoj metodi kao bilo koji drugi objekat. Mislim da je vrijeme za primjer: toast = Proc.new do puts 'Cheers! 'Cheers!' ' end toast.call toast.call toast.call
Ispis: Cheers! Cheers! Cheers!
Dakle, kreirao sam - Proc- (što bi trebalo da je skraćenica od „ procedure“, ali što je još važnije, rimuje se sa „block“) koji je sadržavao jedan blok koda, a nakon toga sam pozvao proceduru tri puta. Primjetit ćete da je plaho nalik na metode. Ustvari, procedure su još sličnije metodama nego sam vam ja to pokazao u ovom primjeru, jer blokovi mogu sadržavati parametre: doYouLike = Proc.new do |aGoodThing| puts 'I 'I *really* like '+aGoodThing+'! '+aGoodThing+'!' end doYouLike.call 'chocolate 'chocolate' ' -62-
doYouLike.call 'ruby 'ruby' '
Ispis: I *really* like chocolate! I *really* like ruby!
Dobro,...vidjesmo šta su procedure i blokovi, kako ih koristiti,...a da li smo shvatili poentu? Zašto jednostavno ne koristiti metode? E, pa...zato što postoje neke stvari koje s metodama jednostavno ne možete učiniti. Da budem precizniji, ne možete uvoditi jednu metodu u drugu (ali procedure u metode možete), a metode ne mogu kao povratnu vrijednost dati druge metode (ali mogu vraćati procedure). Da pojednostavim, razlog je što su procedure objekti, a metode to nisu. (Usput, je li vam poznat išto od ovoga? Aha, vidjeli ste vi blokove i ranije...kad smo učili iteratore . Hajde da popričamo malo više o tome.)
Metode koje prihvaćaju procedure Kad proceduru uvrstimo u neku metodu, onda možemo kontrolisati kako ili koliko puta ćemo pozvati tu proceduru. Naprimjer, recimo da nešto želimo uraditi prije i nakon što se dio koda izvrši: def doSelfImportantly someProc puts 'Everybody 'Everybody just HOLD ON! I have something to do...' do...' someProc.call puts 'Ok 'Ok everyone, I\'m done. Go on with what you were doing.' doing.' end sayHello = Proc.new do puts 'hello 'hello' ' end sayGoodbye = Proc.new do puts 'goodbye 'goodbye' ' end doSelfImportantly doSelfImportantly sayHello doSelfImportantly doSelfImportantly sayGoodbye
Ispis: Everybody just HOLD ON! I have something to do... hello Ok everyone, I'm done. Go on with what you were doing. Everybody just HOLD ON! I have something to do... goodbye Ok everyone, I'm done. Go on with what you were doing.
Možda se na prvi pogled ovaj primjer i ne čini tako spektakularnim, ali on to jeste :o). Postalo je uobičajeno u programiranju imati striktne zahtjeve kad se šta treba izvršiti. Ako, naprimjer, želite spasiti neki fajl, morate otvoriti fajl, ispisati informacije koje želite imati, i onda zatvoriti fajl. Ako ga zaboravite -63-
zatvoriti, svašta bi se moglo desiti. No, svaki put kad želite spasiti ili učitati fajl, morate uraditi opet isto: otvoriti ga, izvršiti unos ili izmjenu, a onda ga zatvoriti. Sve je to naporno i brzo se zaboravlja. U Ruby, spašavanje (ili učitavanje) fajlova funkcioniše na sličan način kao u prethodnom primjeru, tako da se ne morate brinuti nizašta drugo osim onoga što ustvari želite spasiti/sačuvati. (U narednom poglavlju ću vam pokazati gdje ćete naći stvari koje će vam pomoći da uradite stvari kao što je spašavanje ili učitavanje fajlova). Takođe možete napisati i metode koje će utvrditi koliko puta ili čak da li uopšte pozvati proceduru. Evo metode koja će pozvati proceduru za upola manje vremena i još jedne koju ćemo pozvati dvaput: def maybeDo someProc if rand(2 rand(2) == 0 someProc.call end end def twiceDo someProc someProc.call someProc.call end wink = Proc.new do puts ' '' ' end glance = Proc.new do puts ' '' ' end maybeDo maybeDo twiceDo twiceDo
wink glance wink glance
Ispis:
(Na originalnoj stranici [ http://pine.fm/LearnToProgram/] vidjet ćete svaki put kad pristupite stranici ili uradite refresh, da će se pojaviti drukčiji rezultat.) rezultat.) Ovo su neke od raširenijih raširenijih primjena procedura, koje nam daju mogućnost da uradimo stvari koje ne bismo mogli koristeći samo metode. Naravno, mogli bismo napisati metodu da namigne dvaput, ali ne bismo mogli napisati metodu da uradimo „nešto“ dvaput! Prije nego nastavimo, još jedan mali primjer. Do ovog momenta, procedure su prikazane na prilično sličan način, kad se uzajamno porede. Ovaj put će biti znatno drukčije, da biste vidjeli koliko metoda zavisi od procedura koje su u njoj sadržane. Naša metoda će tretirati objekat i proceduru,...i pozvati proceduru nad tim objektom. Ako procedura vrati „false“, prekidamo aplikaciju. u suprotnom pozvaćemo proceduru s vraćenim objektom. Proces će trajati sve dok ne dobije povratnu vrijednost „false“ (koju je bolje da uradi što prije, da program ne padne). Metoda treba da vrati poslijednju vrijednost koja nije „false!“. -64-
def doUntilFalse firstInput, someProc input = firstInput output = firstInput while output input = output output = someProc.call input end input end buildArrayOfSquares buildArrayOfSquares = Proc.new do |array| lastNumber = array.last if lastNumber <= 0 false else array.pop # Uzmemo poslijednji broj u nizu... array.push lastNumber*lastNumber lastNumber*lastNumber # ...zamijenimo ga njegovim kvadratom array.push lastNumber-1 lastNumber-1 # ...nakon čega slijedi broj manji za 1 number. end end alwaysFalse = Proc.new do |justIgnoreMe| false end puts doUntilFalse([5], doUntilFalse([5], buildArrayOfSquares).inspec buildArrayOfSquares).inspect t puts doUntilFalse('I\'m doUntilFalse('I\'m writing this at 3:00 am; someone knock me out!', out! ', alwaysFalse)
Ispis: [25, 16, 9, 4, 1, 0] I'm writing this at 3:00 am; someone knock me out!
Dobro, bijaše to prilično čudan primjer, priznajem. Međutim, ovaj primjer pokazuje koliko su različite primjene metoda kad im se daju različite procedure.
inspect- je vrlo slična metodi - to_s-, osim što na string koji vraća pokušava pokazati kod koji je Metoda – inspect namijenjen za izgradnju objekta koji smo mu proslijedili. U ovom primjeru, pokazuje nam čitav niz (array), nakon što prvi put pozovemo metodu doUntilFalse. Takođe, možete uočiti da ustvari nikad nismo kvadrirali nulu na kraju niza, ali kako je kvadrat nule opet nula, nismo ni morali. Pošto je alwaysFalse uvijek netačno, doUntilFalse metoda nije drugi put uradila apsolutno ništa kad smo je pozvali; samo je vratila ono što smo joj proslijedili. Metode čije su povratne vrijednosti procedure Jedna od mnogih “strava” stvari koje možemo uraditi s procedurama je da ih kreiramo i dobijemo kao -65-
povratnu vrijednost metoda. To nam dozvoljava mnogo lude programerske moći (stvari impresivnih imena, kao što su lijena evaluacija, beskonačne strukture podataka), ali stvar je u tome što ja to skoro nikad ne koristim u praksi, niti se sjećam da sam ikoga vidio da je uradio nešto tako u svom kodu. Mislim da je to jedna od onih stvari koje na kraju i ne iskoristimo u Ruby, ili nas možda Ruby samo ohrabruje da pokušamo naći i drugačija riješanja. Ne znam. U svakom slučaju, samo ću navesti ovo kao opciju. U ovom primjeru, metoda –compose- uzima dvije procedure i kao povratnu vrijednost daje treću, koja, kad se pozove, poziva prvu proceduru i njen rezultat šalje u drugu. def compose proc1, proc2 Proc.new do |x| proc2.call(proc1.call(x)) end end squareIt = Proc.new do |x| x * x end doubleIt = Proc.new do |x| x + x end doubleThenSquare doubleThenSquare = compose doubleIt, squareIt squareThenDouble squareThenDouble = compose squareIt, doubleIt puts doubleThenSquare.call( doubleThenSquare.call(5 5) puts squareThenDouble.call( squareThenDouble.call(5 5)
Ispis: 100 50
Napomena: važno je reći da se – proc1 proc1- morala naći unutar zagrade da bi bila izvršena prije – proc2-. Proslijeđivanje blokova (ne procedura) u metode Dobro, ovo je bio dio zanimljiv za akademsku upotrebu, ali i pomalo mučan za koristiti. Veliki je problem je što postoje tri koraka kroz koja moramo proći (definisanje metode, kreiranje procedure i pozivanje metode koja sadrži tu proceduru), a moglo bi ih biti samo dva (definisanje metode i proslijeđivanje/uvrštavanje bloka koda odmah u nju, bez korištenja procedure), pošto većinom više ne želite koristiti proceduru/blok nakon što ih jednom provučete kroz metodu. Eh, pa, trebali biste znati da je to Ruby sve skontala za nas! Ustvari, svaki put kad smo upotrebljavali iteratore, koristili smo ovo. Prvo ću dati jedan mali primjer, pa ćemo onda na objašnjenja: class Array def eachEven(&wasABlock_nowAPro eachEven(&wasABlock_nowAProc) c) isEven = true # Počinjemo sa "true" jer nizovi počinju brojem 0, koji se smatra -66-
parnim self.each do |object| if isEven wasABlock_nowAProc.call wasABlock_nowAProc.call object end isEven = (not isEven) # Praviti razliku među parnim i neparnim end end end ['apple', ['apple ', 'bad 'bad apple', apple', 'cherry 'cherry', ', 'durian 'durian'].eachEven '].eachEven do |fruit| puts 'Yum! 'Yum! I just love '+fruit+' pies, don\'t you?' you?' end # Upamtite, iz niza uzimamo parne brojeve, a u nizu postoje # i oni koji to nisu,...samo zato što ja volim praviti probleme. [1, 2, 3, 4, 5].eachEven do |oddBall| puts oddBall.to_s+' is NOT an even number!' number!' end
Ispis: Yum! Yum! 1 is 3 is 5 is
I just I just NOT an NOT an NOT an
love love even even even
apple pies, don't you? cherry pies, don't you? number! number! number!
Da bi proslijedili blok metodi – eachEven eachEven-, sve što smo trebali učiniti je zalijepiti taj blok nakon metode. Na ovaj način možete zalijepiti bilo koji blok koda, iako će većina metoda jednostavno ignorisati blok. Da se to ne bi desilo, nego da bi metoda „prigrabila“ blok i pretvorila ga u proceduru, postavite ime te procedure na kraj liste parametara vaše metode, stavljajući stavljajući ispred imena znak ( &). Taj dio je malo zamršen, ali ne i previše, obzirom da ga je potrebno uraditi samo jednom, na početku, pri definisanju metode. Nakon toga možete tu metodu koristiti onoliko puta koliko poželite, baš kao i neke integrisane each- ili – times times- (sjećate li se 5.times do?) metode,...kao metode,...kao što su – each Ako se zbunite na momenat, pomislite samo što bi eachEven trebalo da učini: pozove proslijeđeni blok koda za svaki od elemenata u nizu. Nakon što ste je (metodu) napisali i uvjerili se da radi, nije potrebno da razmišljate razmišljate šta se dešava „pod haubom“ (koji blok se kada poziva??); ustvari, to je upravo razlog zbog kojeg pišemo metod na ovaj način: da ne bismo morali misliti na koji način one rade. Jednostavno, mi ih samo koristimo. Sjećam se da sam pokušavao vidjeti koliko traju pojedini dijelovi programa (što još poznato i kao profiliranje profiliranje koda). Tako sam napisao metodu koja mjeri vrijeme prije izvršavanja koda, onda ga izvršava, a nakon toga ponovo mjeri vrijeme na kraju i ispisuje razlike. Trenutno ne mogu pronaći taj kod, ali mi trenutno i ne treba; vjerovatno je ličio na nešto ovako: def profile descriptionOfBlock, &block startTime = Time.now -67-
block.call duration = Time.now - startTime puts descriptionOfBlock+' descriptionOfBlock+': : '+duration.to_s+' seconds seconds' ' end profile '25000 '25000 doublings' doublings' do number = 1 25000.times do 25000.times number = number + number end puts number.to_s.length.to_s+' number.to_s.length.to_s+' digits digits' ' # That is, the number of digits in this HUGE number. end profile 'count 'count to a million' million' do number = 0 1000000.times do 1000000.times number = number + 1 end end
Ispis: 7526 digits 25000 doublings: 1.06135 seconds count to a million: 0.84302 seconds
Kako je samo jednostavno! Elegantno! Ovom malom metodom, mogu izmjeriti vrijeme između pojedinih odjeljaka odjeljaka bilo kojeg programa. Jednostavno ubacim kod u blok i pošaljem ga na profiliranje. profiliranje. Ima li šta jednostavnije? jednostavnije? U većini programskih jezika, morao bih eksplicitno dodati kod za mjerenje vremena za svai dio programa kojemu sam želio izmjeriti vrijeme. U Ruby, međutim, sve je ne jednom mjestu i što je još važnije – nije mi na putu!
Par stvarčica za oprobati „Đedov sahat “. “. Napravite metodu koja uzima blok koda i poziva ga svaka četiri sata u jednom danu. Naprimjer, ako bismo proslijedili proslijedili blok „do puts 'DONG!' end“, program bi vratio neku vrstu alarma (klinga), kao djedov stari sat. Testirajte svoj kod sa par različitih blokova (uključujući i ovaj koji sam vam dao). Caka: možete iskoristiti Time.now.hour da dobijete trenutno vijeme (sat). Međutim, ova metoda će vam vratiti broj između 0 i 23, tako da ćete morati izmijeniti te vrijednosti da biste dobili standardne vrijednosti koji ima jedan zidni sat,...stari „ Đedov sahat“ (1-12). „ Bilježnik programa “ Napišite metodu koju ćete nazvati – log-, koja daje tekstualni ispis koji opisuje blok koda na kome trenutno radi; slično metodi doSelfImportantly, trebao bi redati stringove koji nam govore kad počne jedan blok, a kad završava, te uz to šta je i povratna vrijednost bloka. Testirajte svoju metodu tako što ćete joj poslati blok nekog koda. Unutar bloka, pozovite se još jednom na - log- proslijeđujući i njemu novi blok (ovakav način integrisanja koda se naziva „gnježdenje“). Drugim riječima, ispis bi trebao izgledati otprilike ovako •
•
Beginning "outer "outer block"... block"... -68-
Beginning "some "some little block"... block"... ..."some little block" finished, returning: 5 Beginning "yet "yet another block"... block"... ..."yet another block" finished, returning: I like Thai food! ..."outer block" finished, returning: false •
“ Bolji bilježnik ”. ”. Ispis koji je imao prošli program je nekako težak za razumjeti i što bi ga više koristili, bivalo bi sve gore. Bilo bi ga daleko bolje shvatiti ako bi se nekako sortirale linije koda tako da možemo razlikovati unutrašnje i vanjske blokove. Da bismo postigli ovo, trebaće nam globalne varijable, one koje možemo vidjeti/pristupiti im u bilo kojem dijeu koda. Globalne varijable se definišu tako da ispred njihovog imena postavimo zna ( $), kao npr. $global, $nestingDepth... Na kraju, bilježnik bi trebao dati ispis sličan ovome: Beginning "outer block"...
Beginning "some "some little block"... block"... Beginning "teeny-tiny "teeny-tiny block"... block"... ..."teeny-tiny block" finished, returning: lots of love ..."some little block" finished, returning: 42 Beginning "yet "yet another block"... block"... ..."yet another block" finished, returning: I love Indian food! ..."outer block" finished, returning: true
Dobro, to bi bilo sve što ćemo naučiti u ovom tutorialu. Čestitam! Dosta ste naučili! Moguće je da ćete trenutno osjećati da se baš svega i ne sjećate, ili da ste određene dijelove preskočili...ali to nema veze, stvarno! Ne radi se u programiranju o tome šta ste zapamtili i šta od toga znate, nego se radi o tome šta možete „skontati“. Sve dok možete i znate gdje naći stvari koje ste zaboravili, ne morate se puno brinuti. Nadam se da ne mislite da sam sve ovo napisao ne gledajući uvijek na poglavlja iza i podsjećajući sebe na neke stvari koje sam zaboravio! Zato što jesam – stalno sam se vraćao na prethodna poglavlja. poglavlja. Dosta mi je pomogao i kod koji je dio ovog tutoriala, kod u vidu primjera. A sad pogledajte gdje sam to ja sve tražio ono što sam na trenutak zaboravio ili gdje sam tražio pomoć kad mi je trebala.
11. Izvan ovog tutoriala Gdje ćemo to sad? Ako imate kakvih pitanja, kome da ih postavite? Šta ako poželite da vaš program otvori web stranicu ili pošalje e-mail ili promjeni dimenzije digitalne fotografije. E pa, postoji mnogo, mnogo, različitih mjesta gdje možete naći pomoć, kad je Ruby u pitanju. Nažalost, to nekako i ne zvuči kao velika pomoć, zar ne? ;-) Za mene postoje tri mjesta na kojima sam siguran da ću pronaći pomoć za Ruby. Ako je neka sitnica u pitanju i mislim da mogu sam eksperimentisati i pronaći odgovor onda koristim IRB. Desi li se da je pitanje nešto malo složenije, pa treba kopati malo dublje, onda koristim vlastitu „ krampu“. Međutim, ako se desi da ni to ne pomaže onda fino pristupim na ruby-talk .
IRB: interaktivna Ruby Kad je na vaš sistem instalirana Ruby, u tom paketu se nalazi i irb – njena interaktivna interaktivna konzola. IRB je jednostavno pokrenuti, na način da u komandni prompt [k.p.: važi i za Linux i za Windows OS] upišete irb [+Enter]. Kad ste u irb-u možete upisati bilo koji izraz i Ruby će vam vratiti vrijednost koja je njegov rezultat. Upišite 1 + 2 i irb vraća rezultat 3 (ne morate čak koristiti puts). Kad završite, jednostavno upišite „exit “. “. Nije ovo sve što irb pruža, ali ostatak možete naučiti koristeći moju „krampu“.
-69-
KRAMPA: „Programming Ruby“ Knjiga koju morate imate je „ Programming Ruby, The Pragmatic Programer's Guide “, koju su napisali David Thomas i Andrew Hunt (Pragmatični programeri). Iako ćete se dobro snaći i sa starijom verzijom, toplo vam preporučujem drugo izdanje ove sjajne knjige, jer ona pokriva poslijednju poslijednju verziju Ruby. Stariju verziju možete dobiti i besplatno, putem interneta, a uz Ruby na Windows-ima trebalo bi da knjiga standardno dolazi. Možete u njoj naći baš sve o Ruby, od početničkog do naprednog. Lahka je za čitanje, razumljiva, ma jednostavno jednostavno savršena! Kamo sreće da svaki programski jezik ima ovako kvalitetnu knjigu kakva je ova. Na poleđini knjige ćete naći ogromnu zbirku koja opisuje do detalja svaku metodu i svakoj klasi, sve potkovano primjerima. Ma ja jednostavno volim ovu knjigu! Nekoliko je adresa preko kojih je možete preuzeti (uključujući i stranicu Pragmatičnih programera), ali meni omiljeno mjesto je ruby-doc.org. Verzija postavljena na toj stranici ima lijepo predočen sadržaj i dobar indeks (a pored toga ima odličnu dokumentaciju koja pokriva sve što se može dovesti u vezu s Ruby). Zašto sam je nazvao „ krampa“? Pa, na naslovnoj strani je nacrtana krampa...možda je smiješno ime. Ruby-Talk: mailing lista za Ruby Ponekad čak i uz irb i "krampu" ne može se naći riješenje za određeni problem. Ili možda želite znati da li je neko prije vas to uradio, tako da vi to ne morate. Ruby-Talk je u tom slučaju prava adresa. Lista je puna prijateljski nastrojenih, pametnih ljudi, koji su spremni pomoći. Više detalja o njoj ovdje ovdje.. Tim Toady Nešto od čega sam vas pokušao zaštiti, ali na što ćete svakako naletiti uskoro, jeste koncept TMTOWTDI (izgovara se kao "Tim Toady"): There's More Than One Way To Do It – postoji više od jednog načina da se to uradi. Neki će reći da je taj koncept prava stvar, dok će drugi dati drukčije mišljenje. Ja lično nešto plaho i ne volim ovaj koncept jer je to užasan način da se nekoga podučava programiranju. (Kao da i ovaj jedan način nije dovoljno izazovan i ponekad zbunjujući.) Međutim, sad kada idete izvan ovog tutoriala, vjerovatno ćete vidjeti mnogo različitih postavki koda za jedan te isti programerski problem. Naprimjer, mogu smisliti najmanje pet različitih načina da ispišem string, na način drukčiji od pukog zarobljavanja zarobljavanja među navode, a svaki od njih radi na drukčiji način. Šest najjednostavnijih sam vam već pokazao.
unless-. Pustiću vas da sami „skontate“ Kad smo pričali ogrananju, pokazao sam vam – if - ali nisam – unless njegovu svrhu koristeći irb. while- je: Još jedna fina „prečica“ koju možete koristiti uz – if -, -, -unless- i – while # Ovo su riječi iz programa koji sam napisao, a koji # generiše riječi na jeziku nalik na engleski. Cool, huh? puts 'grobably 'grobably combergearl kitatently thememberate' thememberate' if 5 == 2** **2 2 + 1** **1 1 puts 'enlestrations 'enlestrationshifter hifter supposine follutify blace' blace' unless 'Chris Chris'.length '.length == 5
Ispis: grobably combergearl kitatently thememberate
Konačno, postoji još jedan način da se napišu metode koje „gutaju“ blokove koda (a ne procedure). Vidjeli smo da smo u jednom primjeru zgrabili blok koda i pretvorili ga u proceduru koristeći &block caku u listi parametara, pri definiciji funkcije. Onda, da bi pozvali taj blok na izvršenje, jednostavno smo dali komandu zbunjujućim). m). Umjesto block.call. E pa, postoji i kraći način da se to učini (iako ga ja lično smatram zbunjujući -70-
ovoga: def doItTwice(&block) doItTwice(&block) block.call block.call end doItTwice do puts 'murditivent 'murditivent flavitemphan siresent litics' litics' end
Ispis: murditivent flavitemphan siresent litics murditivent flavitemphan siresent litics
...napišemo slijedeće: def doItTwice yield yield end doItTwice do puts 'buritiate 'buritiate mustripe lablic acticise' acticise' end
Ispis: buritiate mustripe lablic acticise buritiate mustripe lablic acticise
Ne znam, baš,...šta vi mislite? Možda je samo do mene, ali...YIELD!? Da je bilo nešto kao call_the_hidden_block , ili nešto njemu slično, meni bi imalo više smisla. Mnogo ljudi kaže da YIELD njima ima smisla. Ha, valjda je to sva poenta TMTOWTDI: oni će raditi kako im volja, a ja kako je meni.
KRAJ Iskoristite ovo znanje u korisnu svrhu, a ne za kakvo zlo! :-) Svakako, ako vam je ovaj tutorial bio od pomoći (ili vas je dodatno zbunio, ili nađete grješku), javite mi na e-mail e-mail!!
#. Appendix Ovo dio je namijenjen za izradu zadataka koji se nalaze u tutorialu. Nakon što ih propisno testiram u Ruby, biće objavljeni. Ukoliko imate pitanje ili želite obavješteni kada oni budu postavljeni kontaktirajte me na info u komentaru:
-71-