Home

Skip to end of metadata
Go to start of metadata

Ruby-kielen minispesifikaatio

Ohjelmointikielten periaatteet -kurssin (kevät 2011) harjoitustyö.

Tekijät
  • Mikko Kangasaho
  • Henri Kinnunen
  • Mikko Nieminen
  • Olli Jokinen
Lisenssi


Tämä Ruby-kielen minispesifikaatio on lisensoitu Creative Commons Nimeä-Epäkaupallinen-Tarttuva 3.0 Muokkaamaton lisenssillä.

Sisällysluettelo

1 Ruby-kielen taustaa

1.1 Kielen synty ja historia

Yukihiro Matsumoton aloitti Ruby-kielen kehityksen 24.2.1993. Matsumoto ja Keiju Ishitsuka kehittelivät nimeä verkkokeskustelun aikana ennen kuin kieltä oli varsinaisesti lähdetty edes kehittämään. Lopulta Matsumoto valitsi kielen nimeksi Ruby, koska rubiini (ruby) on hänen erään kollegan syntymäkivi [1].

Yukihiro Matsumotoa pidetään Ruby-kielen isänä ja hänen kantava ajatus kielen kehittämisessä oli luoda kieli, joka yhdistäisi imperatiivisen ja funktionaalisen ohjelmoinnin hyviä puolia. Matsumoto halusi luoda skriptikielen, joka on tehokkaampi kuin Perl, mutta oliomaisempi kuin Python [1]. Ruby sai vaikutteita Matsumoton pitämistä ohjelmointikielistä kuten Perlistä, Smalltalkista, Eiffelistä, Adasta sekä Lispistä [2].

Ensimmäinen versio Ruby-kielestä julkaistiin 25.12.1995 japanilaisessa uutisryhmässä. Rubyn versio oli silloin 0.95 ja kahden päivän sisällä Rubyn julkaistiin kielestä kolme eri versiota. Julkaisu sai aikaan ensimmäisen Rubylle tarkoitetun sähköpostilistan, ruby-listin. Kieli oli jo tässä vaiheessa hyvin pitkälle kehittynyt ja se sisälsi monia nykyäänkin käytettyjä ominaisuuksia kuten erittäin vahvan oliolähtöisen rakenteen, luokat, ja niiden perinnän, mixin-luokat, iteraattorit, sulkeumat (closure), poikkeusten käsittelyn sekä roskien keruun [2].

Version 1.0 julkaistiin 25.12.1996 ja kieli tunnettiin vielä tällöin pääasiassa Japanissa. Version 1.3 julkaisun myötä, vuonna 1999, avattiin ensimmäinen englanninkielinen Ruby-kieleen liittyvä sähköpostilista, Ruby-talk. Tämä kertoi myös kielen kasvavasta kiinnostuksesta muualla maailmassa. Syyskuussa 2000 Rubystä julkaistiin ensimmäinen englannin kielellä painettu kirja Programming Ruby. Kirja julkaistiin myöhemmin vapaasti kaikkien saataville.

Vuonna 2005 Rubyn käyttö lisääntyi, kun Ruby On Rails web-kehys julkaistiin [1].

1.2 Ruby on tulkattava kieli

Ruby on tulkattava kieli. Ruby 1.9:n virallinen tulkki on Koichi's Ruby Interpreter, KRI. Se tunnetaan myös nimellä YARV (Yet Another Ruby VM). Rubylle ei ole olemassa virallista spesifikaatiota, vaan edellä mainittu standarditoteutus toimii de facto -standardina [1]. YARV on kirjoitettu C-kielellä. Se kääntää lähdekoodin ensin tavukoodiksi ja tulkkaa sitten tavukoodia [4].

1.3 Käyttökohteet

Rubyä käytetään yleisesti web-sovellusten tekoon osana Ruby on Rails -ohjelmistokehystä. Esimerkiksi osia Twitteristä on tehty Ruby on Railsillä. Rubyä voidaan käyttää myös Perlin tapaan skriptikielenä, tai sillä voidaan tehdä perinteisiä sovelluksia (desktop application).

Aktiivisia Ruby-projekteja
  • RubyGems - Kirjastojen (gems) hallintaan.
  • JRuby - Javan virtuaalikonetta käyttävä Ruby-toteutus. Mahdollistaa Java-kirjastojen käytön Rubyllä ja Ruby-koodin suorittamisen Javan virtuaalikoneessa.
  • Ruby on Rails - Ohjelmistokehys MVC-arkkitehtuurin web-sovelluksille.
  • Sinatra - Railsia kevyempi web-sovelluskehys.
  • DataMapper - ORM (Object Relation Mapper). Tietokantarajapinta esimerkiksi Sinatra-ohjelmiin.

1.4 Esimerkki Ruby-ohjelmasta

Ohjelman voi ajaa tallentamalla se tiedostoon ja antamalla tiedoston nimi Ruby-tulkille parametriksi. Tietojenkäsittelytieteen laitoksen koneille on asennettu Ruby-tulkki, jolloin tiedostoon fibonacci.rb tallennettu Ruby-ohjelma ajettaisiin komennolla 'ruby fibonacci.rb'. Seuraavan Fibonaccin lukuja laskevan ohjelman alussa on kerrottu Ruby-tulkin sijainti laitoksen ympäristössä, jolloin ohjelman voi ajaa ilman eksplisiittistä Ruby-tulkin käynnistämistä.

Ohjelmia voidaan ajaa myös interaktiivisen Ruby-komentotulkin (interactive Ruby shell, IRB) avulla. Komentotulkki käynnistetään komennolla 'irb' ja kaikki tässä minispesifikaatiossa annetut ohjelmaesimerkit voidaan ajaa siinä kopioimalla ja liittämällä esimerkit tulkkiin.

Ohjelma lukee yhden syöttöluvun, laskee ensimmäisen Fibonaccin luvun, joka on syöttölukua suurempi ja tulostaa kyseisen luvun. Esimerkki ei ole välttämättä tehokkain tapa tehdä asia, mutta se esittelee joitakin Rubylle tyypillisiä käytäntöjä.

Esimerkki 1

2 Alkiorakenne – millaisista palikoista ohjelmat rakennetaan

2.1 Tunnukset

Tunnus voi koostua pelkistä kirjaimista tai kirjainten, numeroiden ja alaviivojen yhdistelmistä. Tunnuksen tulee kuitenkin aina alkaa joko alaviivalla tai kirjaimella. Tunnuksilla ei ole Rubyssä pituusrajoituksia [10, s. 28--29].

Paikalliset muuttujat kirjoitetaan ilman etumerkkiä. Ilmentymä-, luokka- yms. muuttujat esitellään seuraavasti:

muuttuja   Paikallinen muuttuja
@muuttuja  Ilmentymämuuttuja (instance variable)
@@muuttuja Luokkamuuttuja (class variable)
$muuttuja  Yleinen muuttuja (global variable)
VAKIO      Vakioarvot (constants) kirjoitetaan isoilla kirjaimilla

2.2 Varatut sanat ja avainsanat

Rubyssä on määritelty seuraavat avainsanat, joita ei voi käyttää esimerkiksi muuttujien, vakioiden, metodien tai luokkien nimenä [10, s. 30]:

__FILE__ __LINE__ alias   and     BEGIN   begin   break   case    class
def      defined? do      else    elsif   END     end     ensure  false
for      if       in      module  next    nil     not     or      redo
rescue   retry    return  self    super   then    true    undef   unless
until    when     while   yield

2.3 Literaalivakiot

Kokonaislukujen literaalivakiot muodostetaan Rubyssä seuraavasti [10, s. 28]:

123456                 Fixnum
-543                   negatiivinen Fixnum
123456789123345789     Bignum
0xaabb                 Hexadecimal
0377                   Octal
0b001001               Binary

Kokonaisluvut tulkitaan automaattisesti luokaksi Fixnum tai Bignum riippuen vakion arvosta. Vakioilla voi olla etuliitteenä negatiivista lukua ilmaisevan miinus-merkin lisäksi heksadesimaali-, oktaali-, tai binäärilukuja ilmaisevat 0x, 0 tai 0b etuliitteet. Desimaaliluvuissa desimaaliosa erotetaan kokonaisosasta pisteellä.

Merkkijonoliteraalit kirjoitetaan käyttäen heitto- tai lainausmerkkejä: 'esimerkki yksi', "esimerkki kaksi". Erona näissä on se, että heittomerkeillä rajattujen merkkijonovakioiden kohdalla on käytössä suppeampi kontrollimerkistö.

Esimerkki 2

2.4 Erottimet, sisennykset ja rivinvaihdot

Välilyönti ja tabulaattori toimivat erottimina Rubyssä ja niitä voi olla kuinka monta tahansa peräkkäin. Sisennyksillä ei ole semantiikkaa, eli ne eivät vaikuta ohjelman toimintaan millään tavalla. Lauseet päätetään joko puolipisteellä tai rivinvaihdolla. Kuitenkin, jos rivi ei päättynyt puolipisteeseen, eikä kääntäjän mielestä lauseesta muodostu järkevää kokonaisuutta rivinvaihtoon mennessä, jatketaan tulkkaamista seuraavalta riviltä [10, s. 32--33].

2.5 Yleisiä tyyliseikkoja

Rubyn kääntäjä ei ole nirso muuttujien, vakioiden eikä metodien nimien kirjoitustavoista. Yleisesti kuitenkin suositellaan noudatettavan seuraavia ulkoasukäytäntöjä [25]:

  • metodien_ja_muuttujien_nimet_pienellä_ja_välilyönnit_korvattuina_alaviivoilla
  • Luokkien, moduulien yms nimet JavastaTutullaSyntaksilla
  • VAKIOT_ISOILLA_KIRJAIMILLA_JA_ALAVIIVOILLA
  • Sisennyksenä käytetään kahta välilyöntiä
  • Aksessori-metodien nimessä ei käytetä get- tai set-sanoja, vaan metodi nimetään attribuutin mukaan. HUOM! Aksessoreita ei tyypillisesti kirjoiteta, vaan käytetään Rubyn attr_accessor-metodia.
    Esimerkki 3
  • Totuusarvon palautettavien metodien nimessä käytetään kysymysmerkkiä eikä is-sanaa. Eli active? ei is_active.
  • olio.nil? mielummin kuin olio == nil ja taulukko.empty? mielummin kuin taulukko.size == 0 (Railsissa taulukko.blank?, joka on sama kuin taulukko.nil? || taulukko.empty?)

2.6 Arvio kieleen valittujen ratkaisujen eduista ja haitoista

Ruby on oliokieli

Ruby on täysin oliopohjainen kieli. Tämä tarkoittaa sitä, että kaikki tyypit Rubyssä ovat oliotyyppejä. Metodeja voidaan siis kutsua myös esimerkiksi numeroille [2]:

Esimerkki 4

Myös boolean-tyypit true ja false sekä tyhjää olio-viitettä kuvaava nil ovat olioita:

Esimerkki 5

true, false ja nil ovat singleton-olioita, eli luokasta on olemassa vain yksi ilmentymä.

Täydestä oliopohjaisuudesta on hyötyä siten, että se tuo kielen abstraktiotason hieman lähemmäs ihmisen ajatusmaailmaa. Kun kaikki tyypit Rubyssä ovat olioita niin aina tiedetään, että käytettävissä ovat tietyt metodit, joilla voidaan havainnoida tai muuttaa olion tilaa. Esimerkiksi Rubyn Object-luokasta löytyy metodit, joilla oliosta voidaan luoda esimerkiksi uusi String-tyyppinen tai Integer-tyyppinen olio.

Rubyn syntaksi helpottaa elämää

Eräs Rubyn eduista on koodin luettavuus. Rubyä on mahdollista kirjoittaa siten, että sen lukeminen on hyvin lähellä englanninkielisen tekstin lukemista. Esimerkkinä:

Esimerkki 6

Eli "do something unless word includes ord". Tällainen ehtolause on helppo lukea, koska se on niin lähellä sellaista tekstiä, jota ihminen on tottunut lukemaan. Tällaisten luettavien ilmausten lisäksi Ruby tukee myös if ehtolauseita. Toisaalta Rubyn salliman syntaksin monimuotoisuus helpottaa ihmisen työtä kielen kirjoittamisessa, mutta toisaalta se taas vaikeuttaa esimerkiksi Rubyn kääntämistä välikielelle. Käännöksen sisäänlukuvaiheessa on otettava huomioon erilaiset tavat ilmaista kontrollirakenteita.

3 Tunnusten näkyvyysalueet

3.1 Lohkorakenne

Rubyssä lohkona voi olla moduuli, luokka, metodi, lause tai lauseryhmä [10, s. 35]. Lohko kirjoitetaan avainsanojen (esim. def ... end) tai välimerkkien (esim. { ... }) väliin ja sen sisältämä koodi sisennetään kahdella välilyönnillä.

Esimerkki 7

Tyypillisesti, Rubyn tapauksessa, lohkosta puhuttaessa tarkoitetaan sulkeumalle välitettyä funktiota.

3.2 Sulkeumat (closure)

Sulkeuma on koodi, joka välitetään funktiolle parametrina. Tavallisesti se annetaan lohkona, joka erotetaan aaltosulkeilla tai avainsanoilla "do ... end". Tulkin kannalta erottimilla ei ole väliä, mutta yleinen tapa on kirjoittaa yksiriviset lohkot aaltosulkeiden ja useampiriviset "do ... end" väliin [10, s. 141]:

Esimerkki 8

Esimerkin managers-metodi palauttaa parametrina saadusta emps-taulukosta sellaiset alkiot, joille manager?-metodi palauttaa arvon true. Kyseinen toiminnallisuus toteutetaan lohkon avulla. Emps on tässä tapauksessa kokoelma ja siten sille voi kutsua Rubyn Collections-luokan select-metodia. Select saa parametrina lohkon, jossa on määritelty parametri |e|. Eli kun select-metodi iteroi kokoelman läpi, niin select välittää jokaisen elementin parametrina saamalleen lohkolle (eli select kutsuu jokaiselle elementille lohkoa). Tämän jälkeen lohkon sisältö suoritetaan ja arvo palautetaan kutsuvalle ohjelmalle.

Print-managers-metodi toimii vastaavalla tavalla ja siinä läpikäydään managers-metodin palauttama taulukko sekä tulostetaan kaikki John-nimiset työntekijät.

Lohkojen eräs kiinnostava ominaisuus on, että lohkojen sisältä pääsee käsiksi samalla ohjelmalohko-tasolla määriteltyihin muuttujiin. Esimerkiksi:

Esimerkki 9

Salary_treshold on metodin paikallinen muuttuja ja siihen päästään käsiksi samassa metodissa määritellyn lohkon sisältä.

Martin Fowler [9] määrittelee tekstissään kaksi sulkeumille tärkeää piirrettä: ensinnäkin sulkeuma on "koodilohko", joka on sidottu kutsuympäristöönsä. Toisekseen sulkeuman toteuttavat kielet toteuttavat ne yleensä siten, että niiden käyttö on syntaktisesti yksinkertaista. Tästä syystä Fowlerin mukaan sulkeumia on luonnollista käyttää ja tyypillisesti niiden avulla toteutetaan monia asioita kielissä, jotka tukevat sulkeumia.

Proc

Rubyssä on mahdollista luoda sulkeuma myös Proc.new avulla. Tämän ominaisuuden avulla ohjelmoija voi sijoittaa haluamansa sulkeuman mihin tahansa muuttujaan.

Esimerkki [9]:

Esimerkki 10

Tässä määritellään aluksi metodi paid_more, joka luo uuden sulkeuman. Kyseinen uusi sulkeuma tulee suorittamaan parametrina saamalleen oliolle lauseen e.salary > amount. High_paid-muuttuja luodaan kutsumalla paid_more(150). Tämä metodikutsu sitoo arvon '150' paid_more-metodin amount-muuttujaan. Tämä on kiinnostavaa, koska tuo arvo 150 on sidottu amount-muuttujaan niin kauan kuin kyseinen high_paid-muuttuja on olemassa.

Lambda

Kolmas tapa luoda sulkeumia on lambda [10, s. 192]. Lambda on hyvin lähellä Proc-luokkaa, mutta niissä on joitakin eroja. Proc-oliot eivät ole metodeita, vaan koodin pätkiä, joita suoritetaan muun koodin välissä. Lambda on taas anonyymi metodi, jota kutsutaan [10, s. 197--200].

Lambda-lohko, kuten muutkin metodit, tarkistaa parametrien lukumäärän ja aiheuttaa poikkeuksen, jos lukumäärä on väärä. Procin tapauksessa puuttuvan parametrin arvoksi tulee nil ja ylimääräiset parametrit jätetään huomioimatta.

Seuraavassa esimerkissä args-metodissa kutsutaan parametrina saatua lohkoa parametreilla (1, 2) [10, s. 200]. Lohkolle on määritelty kolme parametria, joten esimerkistä selviää Procin ja lambdan ero poikkeustilanteessa:

Esimerkki 11

Toinen ero on, että Proc ylikirjoittaa kutsutun lohkon return-metodin ja lambda ei. Kaksi seuraavaa esimerkkiä havainnollistavat tätä eroa [10, s. 197]:

Esimerkki 12

Proc_return-metodin suoritus lakkaa, kun samalla tasolla määritellyn Proc-olion suoritus päättyy return-lauseeseen. Metodin paluuarvo on kyseisen Proc-olion suorituksessa palautettu arvo "Proc.new". Lambda_return-metodissa taas metodin suoritus ei pääty lambdan sisällä olevaan return-lauseeseen, vaan metodi palauttaa arvon "lambda_return method finished".

Metodin käyttö sulkeumana

Rubyssä myös tavallisia metodeita voidaan käyttää sulkeumina. Koska myös lambdat ovat metodeja, ei metodien käyttö poikkea lambdan käytöstä. Metodi-olioihin pääsee käsiksi Rubyn method-metodilla [10, s. 203].

Esimerkissä Array-luokkaan määriteltyä iterate!-metodia kutsutaan sulkeumalla, joka on määritelty square-nimisenä metodina. Metodin käyttö sulkeumana on järkevää, jos sulkeuman koodia käytetään muualla ohjelmassa (DRY, Don't repeat yourself).

Esimerkki 13

3.3 Sidonta

Ruby on dynaamisesti tyypitetty kieli, eli muuttuja voi viitata kaikentyyppisiin olioihin. Muuttuja, ja sen viittaama olio, sidotaan dynaamisesti ajon aikana [1] .

Seuraavia kappaleita ja esimerkkejä lukiessa on syytä muistaa, että vaikka Rubyllä voi ohjelmoida myös proseduraalisesti, on kyseessä silti oliokieli, jossa kaikki muuttujat ja funktiot kuuluvat johonkin luokkaan. Vaikka funktiota tai muuttujaa ei määrittelisi minkään luokan sisällä, tulkitsee Ruby sen kuuluvan luokkaan Object.

Esimerkki 14

Staattinen sidonta

Rubyssä on staattinen muuttujien sidonta. Paikallinen muuttuja näkyy siinä lohkossa, jossa se on määritelty. Ilmentymämuuttuja näkyy kaikkialla olion sisällä, ja globaali muuttuja näkyy ohjelman ajon aikana kaikkialla [6]. Esimerkiksi seuraava koodi ei toimi, sillä aliohjelma ei näe aliohjelman ulkopuolella määriteltyä paikallista muuttujaa, vaan Ruby olettaa sen olevan self-olion metodikutsu:

Esimerkki 15

Jos aliohjelman täytyy nähdä sen ulkopuolella määritelty muuttuja, täytyy muuttujan olla joko olion ilmentymämuuttuja tai globaali muuttuja. Seuraava esimerkki toimii odotetusti:

Esimerkki 16

Poikkeus näkyvyyssäännöistä: Lohkot

Sulkeumien lohkoilla on omat näkyvyyssääntönsä [10, s. 142--143]. Lohkoissa määritellyt muuttujat näkyvät vain lohkon sisällä, mutta lohkon ulkopuolella samalla tasolla lohkon kanssa määritellyt muuttujat näkyvät lohkon sisällä. Lohkot ja Proc-oliot eroavat tässä suhteessa metodeista. Jos lohkon sisällä sijoitetaan arvo muuttujaan, joka on määritelty lohkon ulkopuolella, niin uutta paikallista muuttujaa ei luoda, vaan ulkopuolella määriteltyyn muuttujaan oikeasti sijoitetaan uusi arvo. Esimerkki selventää (huom! vain Ruby 1.8 ja aikaisemmat versiot!):

Esimerkki 17

Ruby 1.9:ssä lohkon sisäiset muuttujat ovat aina paikallisia. Muuttujan esittely |muuttuja| -syntaksilla luo Ruby 1.9:ssä aina uuden paikallisen muuttujan. Edellä oleva esimerkki olisi Ruby 1.9:ssä:

Esimerkki 18

Jos koodin haluttaisiin toimivan Ruby 1.9:ssä kuten aikaisemmissa Rubyn versioissa, täytyy olla määrittelemättä muuttujaa |muuttuja| -syntaksilla, vaan viitata suoraan ulkopuoliseen muuttujaan:

Esimerkki 19

Staattisen sidonnan poikkeuksia

Rubyssä on myös mahdollista tallentaa paikallinen sidonta Binding-olioon. Tällöin jonkin lausekkeen arvo voidaan arvioida Binding-olion luontihetkellä voimassa olleessa sidonnassa. Seuraavassa esimerkissä muuttuja a ei normaalisti olisi käytettävissä metodin foo ulkopuolella, mutta koska palautamme sen hetkistä sidontaa mallintavan Binding-olion voimme tulostaa a:n arvon metodin ulkopuolella. Metodi eval laskee annetun lausekkeen arvon annetussa sidonnassa [7]. On syytä huomata, että esimerkissä sidonta on vielä olemassa, vaikka funktiosta ollaan jo poistuttu.

Esimerkki 20

3.4 Ensimmäisen, toisen ja kolmannen luokan arvot

Jos ohjelmointikielessä arvon voi sijoittaa muuttujaan, välittää parametrina ja palauttaa funktion arvona, kutsutaan arvoa kyseisessä kielessä ensimmäisen luokan (first class) arvoksi. Rubyssä kaikki arvot (kokonaisluvut, reaaliluvut, totuusarvot jne.) ovat olioita. Kaikille olioille pätee ensimmäisen luokan arvojen säännöt, joten Rubyn arvot ovat ensimmäisen luokan arvoja.

Esimerkki 21

Esimerkissä Fixnum-tyyppiseen muuttujaan y asetetaan arvo 3 ja muuttuja välitetään aliohjelmalle. Aliohjelma tulostaa muuttujan arvon ja palauttaa uuden numeron, jonka tyyppi on myös Fixnum. Muuttujien arvoksi voi asettaa myös funktioita, kuten seuraavassa esimerkissä käy ilmi.

Esimerkki 22

Ruby on siten myös funktioiden suhteen ensimmäisen luokan kieli.

3.5 Arvio kieleen valittujen ratkaisujen eduista ja haitoista

Rubyyn valitut muuttujien näkyvyysalueet johtuvat siitä, että Ruby on puhdas oliokieli. Tästä etuna on kapselointi, jolloin metodit eivät vahingossa muokkaa niiden ulkopuolella määriteltyjä tietorakenteita. Ensimmäisen luokan kielenä Rubyssä voi palauttaa normaalien luokkien lisäksi myös funktioita. Tämä on funktionaaliselle kielelle varsin luonnollista, mutta virhetilanteiden selvitysten ja aloittelevan ohjelmoijan kannalta tämä luo lisähaasteita.

Haasteita tuo myös sulkeumien runsas käyttö, mihin kokenutkaan ohjelmoija ei ole välttämättä tottunut. Sulkeumia käytetään johdonmukaisesti kaikkialla Rubyssä, joten sulkeumien käytön opettelu kannattaa. Sulkeumat eivät myöskään ole Rubyn erikoisuus, vaan ne ovat mukana myös mm. Pythonissa, Perlissä ja JavaScriptissä. Toisaalta sulkeumien avulla saadaan luotua hyvin elegantteja sekä usein paljon lyhyempiä ratkaisuja, kuin kielissä joissa ei ole mahdollista käyttää sulkeumia.

4 Kontrollin ohjaus

4.1 Valinta

Rubyn kieliopissa on tyypillisesti monta tapaa ilmaista kontrollirakenteita. Alla esitellyt tavat eivät ole ainoita mahdollisia. Esimerkkejä lukiessa on syytä muistaa, että kaikki paitsi nil tai false arvioituu Rubyssä trueksi.

if

If-lause toimii Rubyssä lähes samoin kuin monissa muissakin ohjelmointikielissä. Seuraavat neljä esimerkkiä [10, s. 119] kasvattavat muuttujan x arvoa yhdellä jos se on alle 10:

Esimerkki 23

Ehtolauseen voi antaa myös suoritettavan koodin lopuksi [10, s. 121]. Jos koodi on vain yhden rivin mittainen voidaan if-lause kirjoittaa ilman mitään erotinta. Selkeyden vuoksi on hyvä käyttää tätä muotoa vain jos suoritettava koodi on yhden rivin pituinen. Jos suoritettava koodi on useamman rivin mittainen täytyy koodi laittaa begin--end -lohkon tai sulkujen sisään. Seuraavat esimerkit valaisevat:

Esimerkki 24

If-lause Rubyssä palauttaa myös arvon:

Esimerkki 25

else

If-lauseeseen voi liittyä else-haaraa. Jos if-lauseen ehto arvioituu epätodeksi, niin else-haara suoritetaan. Else-haaran suoritettava koodi täytyy erottaa else-avainsanasta samoin kuin if-lause sitä seuraavasta koodista: rivinvaihdolla, puolipisteellä tai then-avainsanalla [10, s. 119]. Seuraavassa esimerkissä x lisätään taulukkoon data, jos taulukko data on olemassa. Jos se ei ole, niin se luodaan ja x asetetaan taulukkoon.

Esimerkki 26

elsif

Jos testattavia ehtoja on monta voidaan käyttää elsif-lausetta (huom! elsif ei ole kirjoitusvirhe vaan Rubyssä varattu sana on todellakin elsif eikä elseif) [10, s. 120]:

Esimerkki 27

unless

Unless on if-lauseen negaatio, eli jos annettu ehto arvioituu epätodeksi tai nil:ksi, annettu koodi suoritetaan. Unless-ehtolause ja suoritettava koodi erotetaan toisistaan samoin kuin if-lauseen tapauksessa [10, s. 122--123]. Seuraavissa esimerkeissä muuttujan x arvoa kasvatetaan yhdellä jos x ei ole suurempi kuin yhdeksän:

Esimerkki 28

Ehtolauseen voi antaa myös viimeisenä aivan kuten if-lauseenkin kanssa [10, s. 122]:

Esimerkki 29

Unless-valintaan voi liittyä myös else-haara [10, s. 122]:

Esimerkki 30

case

Case-lauseen avulla voidaan valita monesta vaihtoehdosta yksi. Rubyssä myös case-lause palauttaa arvon, kuten seuraava esimerkki [10, s. 123] osoittaa:

Esimerkki 31

Case-lauseen ehto voi sisältää myös monta ehtoa. Jos yksikin ehdoista arvioituu todeksi, niin vastaava koodi suoritetaan. Ehdot voi erottaa toisistaan pilkulla tai ||-notaatiolla [10, s. 124].

Esimerkki 32

Jos case-lauseen ehdoissa toistuu sama olio vertailun vasempana osapuolena, on case-lause mahdollista lyhentää antamalla vertailtava olio case-lauseen alussa [10, s. 124]:

Esimerkki 33

case-lause ja === operaattori

Case-lause käyttää vertailussa === operaattoria jos vertailtava objekti annetaan heti case avainsanan jälkeen. === operaattori toimii useimmiten kuten == operaattori, mutta joillekin luokille se on määritelty eri tavoin. Edellinen koodiesimerkki on siis ekvivalentti seuraavan esimerkin kanssa [10, s. 125]

Esimerkki 34

Esimerkiksi luokalle Class === operaattori on määritelty siten, että se testaa onko oikeanpuolimmainen arvo ilmentymä samasta luokasta kuin self-olio:

Esimerkki 35

Javassa ja C-sukuisissa kielissä switch-lauseessa kontrolli voi pudota casen läpi. Rubyssä kuitenkin suoritetaan aina vain yksi koodinpätkä, ja sen jälkeen kontrolli poistuu case-lauseesta.

?:-operaattori

?:-operaattori toimii kuten if-lause. Operaattorille annetaan kolme operandia ja se arvioi niistä ensimmäisen arvon. Jos arvo on mitään muuta kuin false tai nil, niin suoritetaan toinen operandi. Jos ensimmäinen operandi arvioituu falseksi tai nil-olioksi, lausekkeen arvoksi tulee kolmas operandi [10, s. 127]. Seuraava esimerkki tulostaa "Sinulle on yksi uusi viesti.", jos muuttujan n ja kokonaisluvun 1 vertailu arvioituu trueksi. Jos n on esimerkiksi merkkijono "hulabaloo", niin koodi tulostaa "Sinulle on hulabaloo uutta viestiä".

Esimerkki 36

4.2 Toisto eli iteraatio

Rubyssä on kolme iteraatio-lausetta: while, until ja for. Käytännössä iterointiin käytetään kuitenkin yleensä iteraattoreita [10, s.127].

while ja until

While ja until -lauseet suorittavat annettua koodia niin kauan kuin annettu ehto on voimassa (while-lause) tai kunnes ehto tulee todeksi (until-lause) [10, s.127]. Seuraavassa esimerkissä esitellään kaksi tapaa tulostaa numerot yhdestä viiteen:

Esimerkki 37

Jos suoritettava lohko sisältää vain yhden lausekkeen, voidaan while ja until kirjoittaa lauseen loppuun [10, s.128]. Seuraavassa esimerkissä sekä while- että until-lauseet tulostavat numerot ykkösestä viitoseen:

Esimerkki 38

for/in

For-silmukalla voi iteroida läpi jonkin kokoelman alkiot. For-silmukka toimii vain kokoelmille, joille on määritelty iteraattorin palauttava each-metodi [10, s. 129]. Muuttuja, johon alkiot sijoitetaan, ja silmukan sisällä määritellyt muuttujat ovat käytettävissä myös silmukan ulkopuolella. Myös silmukan ulkopuolella määritellyt muuttujat ovat käytettävissä silmukan sisällä. Seuraava koodi tulostaa kaikki taulukon alkiot:

Esimerkki 39

for- ja each-silmukoiden ero

Each-metodille parametrina annettava lohko kuuluu eri nimiavaruuteen. Kuitenkin, kuten lohkojen kohdalla yleensäkin, samalla tasolla sen kanssa määritellyt muuttujat ovat näkyviä [10, s.130]. Edellisessä esimerkissä siis alkio ja silmukan_sisalla_maariltelty_muuttuja eivät näy silmukan ulkopuolelle, mutta silmukan_ulkopuolella_maaritelty_muuttuja kyllä näkyy silmukan sisällä.

Esimerkki 40

4.3 Iteraattorit

Rubyssä on tyypillistä käyttää iteraattoreita silmukoiden toteuttamiseen. Iteraattorit saavat parametrinaan koodilohkon, jonka ne sitten suorittavat [10, s.130]. Esimerkiksi metodit next ja each ovat iteraattoreita:

Esimerkki 41

Muita hyödyllisiä iteraattori-metodeita ovat muun muassa select ja reject. Select valitsee taulukkoon kaikki arvot, joille annetun lohkon suorittaminen arvioituu todeksi, reject taas kaikki jolle annettu lohko arvioituu epätodeksi:

Esimerkki 42

Numeeriset iteraattorit

Loop-metodi toimii ikuisena silmukkana. Sen suorittaminen voidaan keskeyttää esimerkiksi returnilla tai breakillä. Kokonaisluvuille on määritelty upto-iteraattori, joka suorittaa annetun lohkon kaikille luvuille kutsuttua oliota vastaavan luvun ja parametrina annetun luvun väliltä. Seuraava koodi tulostaa numerot yhdestä viiteen:

Esimerkki 43

downto-metodi toimii päinvastoin. Kokonaisluvuille määritelty times-metodi kutsuu annettua lohkoa lukua vastaavan määrän.

4.4 Rekursio

Yleistä

Rekursio on tapa ratkaista ongelmia tietojenkäsittelyssä. Rekursiivinen funktio jakaa isomman ongelman pienempiin osiin ja ratkaisee aliongelman kutsumalla itseään. Kun kutsuketju on mennyt niin pitkälle, että yhtään aliongelmaa ei voida enää ratkaista, niin sen jälkeen rekursiivinen funktio palauttaa arvot kutsuketjun päästä aina rekursiivisen funktion ensimmäiseen kutsuun asti.

Rekursiivinen funktio koostuu yhdestä tai useammasta perusosasta (basecase) sekä yhdestä tai useammasta rekursiivisesta osasta (recursive case). [28]

Häntärekursio ja häntäkutsu

Häntäkutsu on funktiokutsu, joka palauttaa arvon, jonka kutsuva funktio palauttaa. [29] 'Häntä' nimitys tulee siitä, että kutsukohta sijaitsee funktion häntäpäässä, eli lopussa. Häntäkutsut ovat tärkeitä, koska ne voidaan toteuttaa kutsupinossa ilman uuden aktivaatiotietueen lisäystä. Kun suoritettavassa funktiossa edetään aliohjelman kutsuun, voidaan kutsuttavan aliohjelman aktivaatiotietue sijoittaa kutsuvan ohjelman aktivaatiotietueen "tilalle". Eli kutsupinoon ei lisätä uutta aktivaatiotietuetta. [29] Tämä ratkaisu säästää tilaa. Tätä kutsutaan myös termeillä Tail Call Elimination tai Tail Call Optimization. [29]

Häntäkutsu ei ole kielen ominaisuus vaan ennemminkin kääntäjään toteutettava ominaisuus. Tästä syystä kaikki Rubyn ajoympäristöt eivät tue automaattisesti häntäkutsuja. YARV (Yet Another Ruby Virtual machine) tukee häntäkutsuja, mutta ei automaattisesti. YARV pitää kääntää siten, että sille kerrotaan, että häntäkutsut halutaan ottaa käyttöön. Tästä syystä Rubyssa ei usein näe käytettävän häntäkutsuja, koska sen toimintaan kaikilla Ruby tulkeilla ei voi luottaa. Häntärekursio (tail-recursion) tarkoittaa rekursiivista funktiota, jonka lopussa on kutsu itseensä. [29] Rekursion sijasta Rubyssä käytetään yleisemmin iteraatiota, jollaiseksi häntärekursiokin on muutettavissa.

Esimerkki 44

Puurekursio

Esimerkki 45

Yllä olevassa Fibonaccin luvun laskevassa funktiossa 0 ja 1 ovat perusosat ja fib(n-1) + fib(n-2) on rekursiivinen osa. Jos n > 2 niin funktio kutsuu rekursiivisesti fib(n-1) ja fib(n-2). Tästä syntyvää rekursiota kutsutaan puurekursioksi, koska se selvästi haarautuu kahteen puuhun jo ensimmäisessä kutsuvaiheessa. Kyseinen tapa laskea Fibonaccin lukuja on kuitenkin hyvin tehoton. Sen aikavaativuus on exponentiaalinen, eli erittäin huono.

Rubyllä kirjoitettuna kyseinen rekursiivinen funktio voisi näyttää esim tältä:

Esimerkki 46

5 Perustietotyypit

5.1 Perustyypit

Rubyssä ei ole alkeistyyppejä, vaan kaikki tyypit ovat oliotyyppejä.

Esimerkki 47

Rubyn perustyyppejä ovat numerot, merkkijonot, säännölliset lauseet, taulukot, arvoalueet, symbolit ja silput (hash).

5.2 Perinteiset tyypit

Numero voidaan antaa erilaisin tavoin: 123, 123.4 1.23e-4, 0xffff (heksadesimaali), 0b0001 (binääri) ja 0377 (oktaali). Numeroiden tyyppi on Fixnum.

Esimerkki 48

Merkkijonot annetaan lainausmerkkien tai heittomerkkien välissä. Merkkijonojen tyyppi on String.

Esimerkki 49

Säännölliset lauseet annetaan /-merkkien välissä ja niiden tyyppi on Regexp.

Esimerkki 50

Taulukot annetaan []-merkkien välissä alkiot pilkulla eroteltuna. Taulukoiden tyyppi on Array.

Esimerkki 51

5.3 Osaväli

Rubyssä arvoalueet ovat oma perustyyppinsä, jonka luokka on Range.

Esimerkki 52

Arvoalueet määritellään antamalla ala- ja yläraja, jotka erotetaan joko kahdella tai kolmella pisteellä. Kaksi pistettä tarkoittaa, että viimeinen arvo kuuluu alueeseen, kolmella pisteellä kirjoitettuna ei.

Arvot voivat olla esimerkiksi numeroita tai merkkijonoja. Arvon kuuluminen alueeseen voidaan testata ===-operaatiolla.

Esimerkki 53

Range-luokka sisältää metodeja, joiden avulla sitä voidaan käyttää erilaisissa valinta- ja toistolauseissa:

Esimerkki 54

5.4 Symbolit

Rubyn erikoisuus ovat symbolit, joita vastaa luokka Symbol. Symbolin nimi on samalla myös sen merkkijonoesitys.

Merkkijono-esityksen lisäksi symbolilla on numeroesitys [12]. Symbolin numeroarvo on järjestelmän määrittelemä ajonaikainen yksilöllinen arvo, joka vaihtelee suorituskertojen välillä. Symboleiden samuutta voidaan siis vertailla ajon aikana niiden numeroarvoilla, mikä parantaa suorituskykyä, koska Fixnum-tyyppisten olioiden vertailu on String-olioiden vertailua nopeampaa [12].

Symboleiden tunnus on kaksoispiste nimen edessä. Ne eivät saa mitään arvoa, eikä arvoa voida syöttää.

Esimerkki 55

Muuttumattomuuden ansiosta symbolit sopivat erittäin hyvin tilanteeseen, jossa konsistentin säilyttäminen on tärkeää. Symbolit ovat vakioita (constants) kevyempi tapa välittää arvoja ja niitä on tavallista käyttää Hash-olion avain-arvoina.

5.5 Hash

Hash-oliot ovat taulukkoihin rinnastettava tietorakenne, mutta joitakin merkittäviä eroja näillä on. Hashia ei ensinnäkään indeksoida nollasta nousevilla numeroarvoilla, vaan jokaisella arvolla on myös avain. Tavallisesti avaimena käytetään symbolia.

Esimerkki 56

Mikään ei toki estä käyttämästä hashia taulukkomaisesti:

Esimerkki 57

Hashista haetaan arvoja kuten taulukosta:

Esimerkki 58

5.6 Lukujen arvoalueet

Rubyssä pienet kokonaisluvut luvut tallennetaan Fixnum-olioihin, ja suuret Bignum-olioihin. Lukujen arvoalue riippuu toteutuksesta. Esimerkiksi Mac OS X:llä Fixnumin pienin arvo on -2^62 ja suurin arvo 2^62-1. Jos muuttujan arvo kasvaa näiden rajojen ulkopuolelle, muunnetaan muuttuja automaattisesti Bignum-olioksi. Bignum-olion arvolla ei ole minimi- eikä maksimirajoja (paitsi käytettävän tietokoneen muisti). Alla olevassa esimerkistä näkee, miten muuttujan y tyyppi vaihtuu Fixnumista Bignumiksi. Rationaaliluvuissa suurimmalla ja pienimmällä arvolla ei ole rajaa, sillä rationaaliluvun osoittaja ja nimittäjä voivat olla Bignum-olioita. Liukuluvuissa käytetään Float-olioita, jonka pienimmän ja suurimman arvon rajat riippuvat ohjelmaa suorittavan laitteiston arkkitehtuurista. Yleisesti ottaen lukujen raja-arvot riippuvat laitteistoarkkitehtuurista.

Esimerkki 59

Ainoastaan Float-luokan luvuille voi tapahtua "perinteisiä" yli- ja alivuotoja, joissa olion arvo hukkuu. Ylivuodon sattuessa arvoksi tulee positiivinen tai negatiivinen ääretön. Alivuodon tapauksessa arvoksi tulee nolla. Fixnumin ylivuodon tapahtuessa olio muunnetaan automaattisesti Bignumiksi. Bignumin tapauksessa alivuodon sattuessa olio muunnetaan automaattisesti Fixnumiksi.

5.7 Tyypitykset

Dynaaminen tyypitys

Ruby on dynaamisesti tyypitetty kieli, joka tarkoittaa, että muuttujien tyyppi voi vaihtua suorituksen aikana. Muuttujille ei anneta tyyppiä määrittelyn yhteydessä, vaan tyyppi päätellään sijoituslausekkeessa [13].

Esimerkki 60

Vahva tyypitys

Ruby on vahvasti tyypitetty kieli, mikä tarkoittaa, että muuttujan tyypin selvittyä muuttujaan kohdistuvat operaatiot pitää olla kyseiselle tyypille sallittuja. Jos esimerkiksi yritetään yhdistää merkkijonoa ja Fixnum-lukua, ei se onnistu ilman eksplisiittistä luvun merkkijonoksi muuttamista [13].

Esimerkki 61

Toinen esimerkki yhdistää dynaamisen ja vahvan tyypityksen:

Esimerkki 62

5.8 Arvio kieleen valittujen ratkaisujen eduista ja haitoista

Tietotyypit

Arvoalueet, symbolit ja hashit nostavat Rubyn oppimiskynnystä, mutta niiden käyttäminen tehostaa kielen käyttöä. Varsinkin symbolien ja hashien käyttö metodien dynaamisten parametrien yhteydessä on erittäin tehokas menetelmä. Eräs Rubyn hieno ominaisuus on sisäänrakennettu tuki säännöllisille lauseille. Monessa kielessä säännöllisten lauseiden käsittely tapahtuu jonkin ulkoisen kirjaston kautta, mutta Rubyssä, kuten Perlissä, säännölliset lauseet ovat rakennettu kieleen sisälle.

Valinta ja iterointi

Valinta on tehty syntaktisen sokeroinnin kautta miellyttäväksi. Iteraattoreiden käyttö on tyylikäs oliomainen ratkaisu. On tyylikkäämpää pyytää oliota itse iteroimaan itsensä, kuin kirjoittaa for-lause, joka tietää olion sisäisestä toteutuksesta jotain.

Tyypitykset

Yhtenä mahdollisena haittana dynaamisesta ja vahvasta tyypityksestä on se, että osa virheistä tulee ilmi vasta suorituksen aikana. Vahvan tyypityksen etuna on kuitenkin se, että tyypille kohdistuvat operaatiot eivät aiheuta ongelmia, koska aina tarkistetaan, että operaatio on tyypille sopiva. Tällöin ei tapahdu esimerkiksi epämääräisiä poikkeustilanteita, tai muuttujat eivät saa omituisia arvoja, koska muuttujille voidaan käyttää ainoastaan saman tyyppisiä operaatioita.

Rekursio

Ruby-kieli nojaa paljon iteraattoreiden käyttöön ja rekursiota näkee paljon harvemmin. Yksi suuri syy on siinä, että häntärekursioon ei voi Rubyssä luottaa, koska sen toiminnasta ei ole varmuutta kaikilla Ruby-tulkeilla. Tästä syystä Rubyllä ei tule vastaan juurikaan häntärekursioon nojautuvia toteutuksia, vaan ennemmin suositaan iteraatiota.

6 Laskennan kapselointi

6.1 Nimetyt aliohjelmat

Rubyssä on metodeja ja Proc-olioita, jotka molemmat ovat nimettyjä koodipätkiä, jotka voivat ottaa vastaan parametrejä. Metodit liittyvät aina johonkin olioon, mutta Proc-oliot eivät. Tästä syystä Proc-olioita voi kutsua funktioiksi[11].

Metodit

Metodit määritellään def-avainsanalla. Ruby on puhdas oliokieli, ja jos metodi määritellään luokkien ulkopuolelle, se tulkitaan kuuluvan Object-luokkaan [11]. Metodin määrittelyn syntaksi on seuraava:

Esimerkki 63

Metodin paluuarvo on viimeiseksi suoritetun rivin arvo. Halutessaan metodista voi palauttaa arvon myös return-avainsanalla. Metodeilla on vain yksi paluuarvo; jos halutaan palauttaa useampia arvoja täytyy arvot laittaa taulukkoon. Tämän voi tehdä myös käyttämällä return-avainsanaa ja erottamalla paluuarvot pilkulla, jolloin niistä muodostetaan taulukko [11].

Esimerkki 64

Metodien määrittely yksittäisille olioille

Metodeja määritellään yleensä luokille, mutta metodeja voi määritellä myös yksittäisille olioille. Tällöin määrittelysyntaksiin täytyy liittää tieto siitä mihin olioon viitataan [11]

Esimerkki 65

Metodeja ei voi ylikuormittaa

Ruby eroaa esimerkiksi Javasta siten, että metodeja ei voi ylikuormittaa. Ylikuormittamista vastaava toiminnallisuus saadaan Rubyssä aikaan sillä, että parametrit voivat olla mitä vain tyyppiä, ja metodin koodissa voidaan tehdä eri asioita riippuen parametrien tyypeistä. Lisäksi parametreille voi antaa oletusarvoja, eikä oletusarvon saavia parametrejä tarvitse antaa metodia kutsuttaessa. Tällöin samaa metodia voidaan kutsua eri määrillä parametrejä [11].

Parametrien oletusarvot

Parametrien oletusarvot annetaan lisäämällä parametrin perään '='-merkki ja antamalla haluttu arvo. Jos metodia kutsutaan ilman, että oletusarvoiselle parametrille annetaan arvo, arvioidaan sille alkuarvo kutsuhetkellä [11].

Esimerkki 66

Parametrien määrä voi olla mielivaltainen

Metodin voi määritellä myös siten, että sille voi antaa mielivaltaisen määrän parametrejä. Tällöin metodin määrittelyssä jonkun parametrin eteen täytyy kirjoittaa *-merkki. Tällöin parametrit asetetaan taulukkoon, jonka nimi on sama kuin parametrin. Seuraava metodi laskee yhteen mielivaltaisen määrän parametrejä ja palauttaa niiden summan:

Esimerkki 67

6.2 Proc-oliot

Proc-oliot ovat sulkeumia, joten niillä voi olla pääsy niiden kanssa samalla tasolla määriteltyihin paikallisiin muuttujiin, vaikka Proc-olion koodi suoritettaisiin eri nimiavaruudessa, kuin missä se luotiin. Luokalla Proc on kahdentyyppisiä ilmentymiä, proc-olioita ja lambda-olioita. Lambda-lohko, kuten muutkin metodit, tarkistaa parametrien lukumäärän ja aiheuttaa poikkeuksen, jos lukumäärä on väärä. Procin tapauksessa puuttuvan parametrin arvoksi tulee nil ja ylimääräiset parametrit jätetään huomioimatta.

Proc-olion voi tallentaa muuttujaan ja sen koodin voi suorittaa muuttujan kautta:

Esimerkki 68

Myös lambda-olion voi tallentaa muuttujaan:

Esimerkki 69

6.3 Metodien sulkeuma

Metodiin voidaan liittää viimeiseksi parametriksi &-merkillä alkava parametri, joka saa arvokseen metodikutsuun liittyvän sulkeuma-lohkon. &-merkillä alkavia muuttujia voi olla vain yksi ja se täytyy sijoittaa parametrilistassa viimeiseksi.

Tyypillisesti, esimerkiksi Rubyn peruskalustossa, parametrin nimenä käytetään &block, mutta mikään ei estä nimeämästä parametria toisella nimellä.

Esimerkki 70

Yield

Kontrolli voidaan siirtää metodista sulkeuma-lohkoon yield-avainsanalla. Ilman sulkeumaa yield aiheuttaa poikkeuksen. Esimerkiksi edellisen kohdan esimerkki voitaisiin kirjoittaa yieldin avulla seuraavasti:

Esimerkki 71

Ohjelman kontrolli siirtyy lohkolle ja palautuu metodille, kun lohko on suoritettu loppuun.

Esimerkki 72

Parametrien välitys lohkolle

Lohkolle voidaan välittää metodista parametreja kutsumalla yieldiä parametreilla.

Esimerkki 73

Vaikka kontrolli voi siirtyä hetkeksi sulkeumalle, on metodeilla normaali paluuarvonsa.

Esimerkki 74

6.4 Hash parametrina


Hash-olioita käytetään Rubyssä parametrien välityksessä. Jos metodille halutaan antaa useita parametreja, joista kaikki eivät ole pakollisia, parametri-arvona voidaan käyttää Hash-oliota. Tällöin avaimina käytetään symboleita, jotka toimivat eräänlaisina parametrien tunnuksina.

Esimerkki 75

6.5 Suoritusaikaisiin virheisiin varautumisen välineet – poikkeukset

Poikkeukset ovat olioita, jotka edustavat jotain poikkeuksellista tilannetta. Kun jokin poikkeuksellinen tilanne tapahtuu, voidaan poikkeus nostaa (raise). Kun poikkeus on nostettu, kontrolli siirtyy poikkeuksenkäsittelijälle. Poikkeuksenkäsittelijässä määritetään miten poikkeuksesta toivutaan. Jos Ruby-tulkki ei löydä sopivaa poikkeuksenkäsittelijää, ohjelma lopetetaan ja poikkeuksen viesti tulostetaan standardisyöttövirtaan.

Poikkeukset ja kontrollirakenteet – rescue, else ja ensure

Seuraavassa esimerkissä metodin alussa on "vartalo", eli koodirivejä, jotka voivat aiheuttaa poikkeuksen. Rescue-lausetta seuraa poikkeuksenkäsittelijä. Else-haara suoritetaan, jos poikkeusta ei ole nostettu, ja ensuren jälkeen tuleva koodi suoritetaan joka tapauksessa viimeisenä ennen kontrollin poistumista metodista [10, s. 164].

Esimerkki 76

Rescue määrittää poikkeuksenkäsittelijän sille koodille, joka on määritelty edellisen begin, def, class tai module -avainsanan ja rescue-avainsanan välissä [10, s. 164]:

Esimerkki 77

Poikkeusten nostaminen – raise

Poikkeuksia nostetaan kutsumalla metodia raise. Raise-metodille voi antaa parametreja seuraavasti:

Esimerkki 78

Poikkeuskäsittelijän löytäminen

Ruby-tulkki hakee poikkeuskäsittelijää ensin siitä lohkosta, jossa poikkeuksen nostanut rivi on. Jos samassa lohkossa ei ole rescue-lausetta, niin tulkki alkaa etsimään sitä ulommasta lohkosta. Näin jatketaan kunnes rescue-lause löytyy, tai ulompia lohkoja ei enää ole. Jos poikkeuksenkäsittelijää ei löydy, ohjelman suoritus päättyy [10, s. 160].

Poikkeus poikkeuksenkäsittelijässä

Jos poikkeuksenkäsittelijässä aiheutuu poikkeus, alkuperäinen poikkeus hylätään ja Ruby-tulkki yrittää löytää uudelle poikkeukselle poikkeuksenkäsittelijän:

Esimerkki 79

Poikkeushierarkia ja omien poikkeuksien luominen

Poikkeushierarkian ylimmällä tasolla on Exception-luokka. Sen suorat aliluokat Ruby 1.9:ssä ovat NoMemoryError, ScriptError, SecurityError, SignalException, SystemExit, SystemStackError ja StandardError. Suurin osa poikkeuksista on luokan StandardError aliluokkia. StandardError edustaa virhettä, joihin ohjelmoija tyypillisesti haluaa kirjoittaa poikkeuksenkäsittelijän. Muiden ylimmän tason luokkien virheet ovat systeemitason virheitä, joille ohjelmoija tyypillisesti voi tehdä joko hyvin vähän tai ei mitään [10, s. 155]. Oman poikkeusluokan voi luoda yhdellä koodirivillä:

Esimerkki 80

6.6 Rinnakkainen laskenta

Ruby ei tue aitoa rinnakkaisuutta, koska Ruby-tulkissa käytetty yleinen tulkkilukko (global interpreter lock, GIL) estää usean Ruby-säikeen yhtäaikaisen suorittamisen [15]. Javan virtuaalikonetta hyödyntävässä JRuby-toteutuksessa tätä ongelmaa ei ole, vaan siinä voidaan käyttää rinnakkaisia säikeitä [15].

Tästä rajoituksesta huolimatta Rubyssä on mahdollista tehdä säikeitä, jolloin ohjelmoija voi ajatella niitä suoritettavan rinnakkain. Jos halutaan tehdä oikeasti rinnakkaisia sovelluksia, täytyy käynnistää useita prosesseja (tai käyttää JRubya).

Säikeet (threads)

Säikeiden hallintaan Rubyssä on luokka Thread, jonka luonti-metodille new annetaan koodilohkona säikeessä suoritettava koodi. Lohkossa käytetyt muuttujat näkyvät myös muille säikeille. Säikeellä voi olla myös omia paikallisia muuttujia (esimerkki kirjasta The Ruby Programming Language [24]).

Esimerkki 81

Vuorottaisrutiinit (coroutines)

Rubyn versiosta 1.9 lähtien vuorottaisrutiineja on voinut luoda Fiber-luokan avulla [14]. Esimerkiksi Fibonaccin lukuja laskeva ohjelma voidaan toteuttaa seuraavalla tavalla [14]:

Esimerkki 82

Avainsana tässä on yield, joka pysäyttää (suspends) rutiinin suorituksen. Kun rutiinin suoritusta jatketaan, palautuu yieldin parametrina annettu arvo. Yield käyttäytyy siis return-lauseen tapaan, mutta yieldin jälkeen rutiinin suoritus jatkuu [14].

Esimerkki 83

6.7 Tyypilliset ohjelma-arkkitehtuurit

Ohjelmointi Rubyllä tapahtuu usen erilaisten ohjelmistokehysten tukemana. Alla on esitelty mm. ohjelmistokehys Ruby on Rails sekä MVC- ja REST-arkkitehtuurit.

MVC ja Ruby on Rails

Rubyä käytetään usein Ruby on Rails (RoR) -kehyksen kanssa. RoR on web-sovellusten tekoon tarkoitettu MVC (Model View Controller) -tyyppinen kehys, jonka tarkoitus on eritellä sovellusten näkymät toimintalogiikasta [18]. MVC-mallissa malli (model) vastaa tietomallia, esim tietokannan yhtä taulua. Näkymä (view) puolestaan on vastuussa kyseisen tietomallin esittämisestä käyttäjälle. Käsittelijä (controller) jää näiden kahden osan väliin, ollen ikään kuin molempia yhdistävä osa. Käsittelijässä voidaan määritellä esimerkiksi mitä tietoja mallin määrittämästä tietomallista halutaan esittää näkymässä.

Representational State Transfer (REST) -arkkitehtuuri

REST-arkkitehtuurin on kehittänyt Roy Fielding [19] [20] ja ideana siinä on käyttää URL-osoitteita kuvaamaan eri toimintoja ja toimintojen kohteita. Myös kohteiden tilojen muutokset välitetään URL-osoitteiden avulla ohjelmiston eri komponenteille. Esimerkiksi kuva-albumien hallintaan tarkoitetussa ohjelmistossa yksittäisen kuvan poisto voisi tapahtua seuraavalla HTTP-protokollan mukaisella komennolla:

Esimerkki 84

REST-arkkitehtuurin toisena ideana on tehdä palvelimen ja sitä kutsuvan ohjelmiston (esimerkiksi selain) välisestä kommunikoinnista tilatonta. Kutsuva ohjelma voidaan ohjata tarvittaessa toiselle palvelimelle ilman, että eri palvelinten välillä tarvitaan tietojen ja istuntojen synkronointia. RESTin muita hyötyjä ovat mm. ohjelmistojen helppo skaalautuminen (ei synkronointitarvetta palvelimien välillä), sekä yhdenmukaiset ja helppolukuiset rajapinnat.

Rubyn ohjelmistokehyksistä mm. Ruby-on-Rails [18] ja Sinatra [21] tukevat REST-arkkitehtuuria.

Putket ja suodattimet

Yksi vahvasti esillä oleva arkkitehtuurityyppi on putket ja suodattimet [17], eli Ruby-ohjelmia ajatellen perinteiset skriptit. Putket ja suodattimet tarkoittaa sitä, että jokainen ohjelma tuottaa jotain, mitä voidaan käyttää seuraavan ohjelman syötteenä. Usein se on dataa, tavuja tai bittejä. Tämän tyyppisiä ohjelmia voidaan Unix-järjestelmissä putkittaa. Unix-järjestelmissä Ruby-skriptejä voidaan ajaa samaan tyyliin kuin Perl-skriptejä, eli skriptitiedosto voidaan määritellä suoritettavaksi ja sen jälkeen suorittaa.

Esimerkki 85

Olio-ohjelmointi

Koska Ruby on täysin oliopohjainen kieli, myös oliopohjainen arkkitehtuuri sopii kielelle luontevasti. Ohjelmat koostuvat siis luokista, jotka mallintavat reaalielämän asioita. Varsinainen ohjelman toimintalogiikka kapseloidaan luokkien sisälle ja logiikkaan päästään käsiksi ainoastaan hyvin määriteltyjen rajapintojen kautta.

Esimerkki 86

6.8 Arvio kieleen valittujen ratkaisujen eduista ja haitoista

On kätevää, että metodien lisäksi voi luoda Proc-olioita, joita voi muunmuassa välittää parametreina. Tämä mahdollistaa geneerisempien metodien kirjoittamisen. Metodikutsuissa ja metodien määrittelyssä on mukavaa syntaktista sokeria, että sulkuja ei ole pakko kirjoittaa, jos ei halua.

Rinnakkaisuuden puutteen takia Ruby-kielellä toteutetut ohjelmat eivät pysty hyödyntämään useita prosessoreja, joten mahdollinen nopeushyöty jää saavuttamatta. Toisaalta, rinnakkaisuuden puuttuminen helpottaa ohjelmoijan työtä, kun lukkiutumia yms. tilanteita, joita rinnakkaisessa laskennassa tulee vastaan, ei tarvitse ottaa huomioon.

Muilla kielillä toteutettuja kirjastoja voidaan integroida Rubyyn helposti, koska esimerkiksi C:llä kirjoitetuissa kirjastoissa ei useinkaan ole otettu rinnakkaisuutta huomioon [16].

7 Datan kapselointi

7.1 Rakenteiset tyypit

Tietue (struct) on Rubyn rakenteinen tyyppi. Sitä käytetään melko samanlaisiin tarkoituksiin kuin silppuja (hash), mutta niillä on eroja [22]. Tietueissa käytetään silppujen tapaan symboleita kuvaamaan avainarvoja. Tietueisiin voidaan liittää toiminnallisuutta, mutta silppuihin ei.

Rubyn tietueet ovat kaikki olioita, mutta kieli tarjoaa välineitä luokkien helppoon määrittelyyn, jolloin ne käytännössä muistuttavat C-sukuisista kielistä tuttuja tietueita. Rubyn tietueet ovat lähempänä C++:n tietueita kuin C:n, sillä ne tarjoavat datan lisäksi myös toiminnallisuutta [22].

Tietueet

Tietuita käyttämällä voidaan luoda nopeasti luokka, joka sisältää luokkamuuttujia ja aksessoreja. Tietue määritellään Struct-luokan new-metodilla, parametrina tietueen kentät symboleina. Tästä tietueesta voi luoda ilmentymiä ja käyttää niitä normaalin olion tavoin [22]. Itse asiassa tietueet ovat tyypiltään Class, eli Struct-luokka on loppujen lopuksi vain syntaktista sokeria.

Esimerkki 87

Tietueet käyttäytyvät taulukon ja silpun tavoin, tietyin rajoituksin [22].

Esimerkki 88

Tietueille voi määritellä toiminnallisuutta antamalla Structin new-metodille koodilohkon, jossa määritellään halutut metodit [22]:

Esimerkki 89

Luetellut tyypit

Ruby ei tarjoa lueteltuja tyyppejä, mutta silppuja, joissa avaimina käytetään symboleja, voidaan käyttää tähän tarkoitukseen [22]. Myös Struct-tyyppiä voidaan käyttää lueteltuna tyyppinä silpun tapaan.

Esimerkki 90

Myös luokan vakioarvoja voidaan käyttää lueteltujen tyyppien määrittelyyn. Vakioiden hyöty verrattuna silppuihin on, että vakioita voidaan käyttää myös muista luokista. Toisaalta silpuissa on each-metodi, jota voidaan käyttää lueteltujen tyyppien iteroimiseen [22].

Edellinen esimerkki käyttäen vakioita:

Esimerkki 91

7.2 Viitesemantiikka

Rubyssä käytetään olioille viitesemantiikkaa, eli muuttujat sisältävät viittauksia olioihin. Sijoituksen a=b jälkeen muuttujat a ja b viittaavat siis samaan olioon. Pienenä poikkeuksena viitesemantiikkaan ovat nil, Fixnum ja Boolean. Näidenkin kohdalla käytetään viitesemantiikkaa, mutta sama arvo kohdistuu aina samaan olioon. Boolean-muuttujan arvon ollessa false viittaa muuttuja aina olioon, jonka tunnus on 0. Boolean arvo true kohdistuu puolestaan aina olioon, jonka tunnus on 2. Vastaavasti muuttujan arvon ollessa nil kohdistuu viittaus olioon, jonka tunnus on 4.

Esimerkki 92

Alla olevalla esimerkillä voidaan todentaa String-olion viitesemantiikka.

Esimerkki 93

7.3 Oliot ja luokat

Oliot Rubyssä

Rubyssä kaikkea käsitellään olioina [24 s. xxiv]. Ruby on aidosti oliokieli. Se ei erittele esimerkiksi Javan tapaan tyyppejä perus- ja oliotyypeiksi. Rubyssä kaikki tyypit ovat olioita. Siten niille on käytettävissä aina samoja välineitä olion tilan tutkimiseen, koska kaikki oliot periytyvät luokasta Object. Olio muodostetaan luokasta kutsumalla sille konstruktoria esim. Human.new.

Oliolla on yksiselitteinen tunniste (Object ID). Alla olevassa esimerkissä nähdään Rubyn tapa käsitellä olioita. Rubyssä esimerkiksi uuden merkkijono-olion sijoittaminen muuttujaan luo aina uuden olion.

Esimerkki 94

Oliolle voi määritellä normaaliin olio-ohjelmointityyliin omia ilmentymämuuttujia. Ilmentymämuuttujien syntaksi Rubyssä on @instance_variable.

Rubyn eräs erikoinen ominaisuus on mahdollisuus määritellä yksittäiselle oliolle omia metodeita (laskennan kapselointi). Tällöin kyseinen metodi on käytettävissä ainoastaan sillä oliolla, jolle se on määritelty.

Esimerkki 95

Rubyn metodeita kutsutaan lähettämälle oliolle viesti, joka sisältää metodin nimen, sekä kaikki kyseisen metodin tarvitsemat parametrit [24 s. 36]. Kun olio saa viestin, tarkistaa se löytyykö kyseisen nimistä metodia sen omasta luokasta. Jos löytyy, metodi suoritetaan. Jos metodia ei löydy, tarkistetaan löytyykö se yliluokasta ja sen yliluokasta ja sen yliluokasta... Jos metodia ei löydy mistään, nostetaan poikkeus [25].

Luokat

Rubyn luokka on myös olio. Se on luokan Class ilmentymä. Luokka sisältää kaikki olioiden ominaisuudet, sekä listan luokalle määritellyistä metodeista sekä viittauksen luokan yliluokkaan [26].

Esimerkki 96

Yllä olevassa esimerkissä on tyypillinen Ruby-luokka, jossa on määritelty konstruktori, yksi luokkametodi ja yksi ilmentymämetodi. Ruby on "kirjoitusystävällinen" tarjoamalla get- ja set-metodien luonnin attr_accessor metodilla. Tälle metodille annetaan parametreina ilmentymämuuttujien nimet symboleina. Tämän jälkeen olion tilaa voi tarkastella ja muuttaa suoraan käyttämällä attribuuttien nimiä.

Esimerkki 97

Luokan näkyvyysmäärittelyt

Ruby tukee luokkien metodien näkyvyysmäärittelyjä oliokielissä tyypillisillä public, protected ja private määreillä. Public ja protected ovat monista kielistä tutut: Public näkyy kaikkialle, myös ulkopuolisille luokille, protected näkyy luokan sisälle ja luokan aliluokille. Protected näkyy myös saman luokan muille ilmentymille. Private on toteutettu Rubyssä siten, että se näkyy oliokohtaisesti vain oliolle itselleen. Ts. private määreellä varustetut metodit eivät näy toisille luokan ilmentymille, vaikka ne olisivat saman luokan ilmentymiä, kuin olio itse.

Luokkien perintä

Rubyssä luokka peritään syntaksilla: class aliLuokka < Luokka. Ruby tukee yksinkertaista luokan perintää, mutta ei luokkien moniperintää. Luokkien moniperinnän sijaan Ruby toteuttaa niin sanotut mixin-luokat [25] . Rubyssä aliluokasta on mahdollisuus päästä käsiksi yliluokan metodeihin super kutsulla. Kun aliluokasta kutsutaan super, niin tällöin kutsutaan yliluokan samannimistä metodia, kuin missä aliluokassa oltiin. Samalla yliluokasta kutsutulle metodille voidaan välittää parametreja.

Yksinkertainen esimerkki luokkien perinnästä Rubyssä.

Esimerkki 98

Ruby ei tue moniperintää, vaan luokalla voi olla vain yksi yliluokka. Rajoitusta voidaan kiertää mixin-luokkien avulla.

Mixin-luokat

Mixin-luokkaa voi ajatella seuraavasti "A class can mixin a module", eli vapaasti käännettynä, luokka voi yhdistyä moduulin kanssa. Moduuli tarkoittaa Rubyssä metodikokoelmaa, jonka sisältämät metodit mixin-luokka saa käyttöönsä. Luokka ei peri näitä metodeita, vaan luokasta on yksinkertaisesti viittaus tähän kokoelmaan. Jos kokoelmaa muutetaan ohjelman suorituksen aikana, kaikkien tätä kokoelmaa käyttävien mixin-luokkien toiminta muuttuu. Moduulit otetaan luokassa käyttöön avainsanalla include.

Esimerkki 99

Mixin-luokkien avulla voidaan saada käyttöön useamman moduulin metodit. Mixin-luokkien hieno ominaisuus on siinä, että se ei aiheuta moniperinnän kaltaisia ongelmia. Timantti-ongelmaa (diamond problem [5]) ei esiinny mixin-luokkien kanssa.

Timanttiongelma tarkoittaa epäselvyyttä, joka liittyy kun luokat B ja C perivät luokan A. Luokka D perii moniperintänä luokan B ja C ja molemmissa luokissa ylikirjoitetaan luokan A metodi equals. Tämän jälkeen, kun luokka D kutsuu luokassa A määriteltyä metodia equals, niin ongelma on valitaanko luokassa B vai luokassa C määritelty metodi. Rubyssä mixin-luokkaan valitaan viimeisimpänä käyttöönotetun moduulin metodi (reverse-inclusion-order depth-first).

Esimerkki 100

7.4 Arvio kieleen valittujen ratkaisujen eduista ja haitoista

Struct

Struct-tyyppi nopeuttaa yksinkertaisten luokkien luomista, koska tyyppi voidaan määritellä yhdellä lauseella, eikä tarvita erillistä class-määritystä. Lisäksi Struct-luokan ilmentymällä on hyödyllisiä sisäänrakennettuja metodeja mm. kenttien iteroimiseen tai käsittelyyn.

Esimerkki 101

Lueteltujen tyyppien puuttuminen

Silpuilla (hash) ja vakioilla voidaan kiertää lueteltujen tyyppien puuttuminen. Olemattomalla avaimella silpusta hakeminen ei kuitenkaan aiheuta poikkeusta, vaan palauttaa arvon nil, kun esimerkiksi Javassa Enumin käyttö virheellisellä "avaimella" aiheuttaa käännösaikaisen virheen. Vakioilla väärinkirjoitetut nimet kuitenkin aiheuttavat ajonaikaisen virheen, mutta vakioita käytettäessä menetetään joitakin silpun tuomia etuja, esimerkiksi arvojen iterointi.

Luokat ja oliot

Rubyn "kaikki ovat olioita" ajattelutapa helpottaa ohjelmoijan työtä, koska tällöin kaikkea ajatellaan olioina. Kaikelle on siis käytössä aina tietyt metodit, joilla voidaan tarkastella olion tilaa. Tällainen ratkaisu pitää kielen abstraktiotason myös perinteisempiä (esim. Java, C++) kieliä korkeampana, sillä Rubyssä ei ole mukana minkäänlaisia perustyyppejä. On ratkaisulla myös huonot puolensa. Muistinkulutus on korkeampi, koska kaikki kääritään aina olioiden sisään. Ruby ei siis ole omimmillaan sellaisissa sovelluksissa, joissa halutaan ehdotonta tehokkuutta. Rubyssä on ajateltu ohjelmoijaa kielen suunnittelussa ja karsittu tehokkuudessa, jotta kielellä olisi mahdollisimman helppo kuvata reaalimaailman ongelmia.

Rubyssä on viety private näkyvyysmääre normaaleja kieliä tiukemmaksi. Rubyn private sallii ainoastaan ilmentymän itsensä käyttää privatella määriteltyjä metodeita. Esimerkiksi Javassa private määreellä varustettua metodia voidaan käyttää myös saman luokan toisesta ilmentymästä. Rubyn ratkaisu lisää luokkien kapselointia ja on yksi keino varmistaa olioiden eheyttä.

Viitesemantiikka on helppo sisäistää jos ohjelmoijalla on tuntemusta esimerkiksi Javasta tai Pythonista. Mallissa on muun muassa se etu, että jokainen olio luodaan eksplisiittisesti ja jokaisen luonnin yhteydessä kutsutaan olion konstruktoria.

8 Yhteenveto

Ruby on nykyaikainen ja monipuolinen oliopohjainen ohjelmointikieli. Kielen syntaksi sallii ohjelman kirjoittamisen helposti luettavassa muodossa, mutta toisaalta monipuolinen syntaksi mahdollistaa saman ohjelman eri osien kirjoittamisen täysin eri tyylisesti. Tämä voi joissain tapauksissa vaikeuttaa ohjelman ymmärrettävyyttä. Ensimmäisen asteen kielenä Rubyssä voi funktioiden paluuarvoina palauttaa funktioita. Tämä on luonnollista funktionaalisten kielten taitajille, mutta uusien käyttäjien ja virheiden selvittämisen näkökulmasta funktionaalisuus ja sulkeumat tuovat kieleen paljon monimutkaisuutta.

Vaikkakin Rubyyn on sisällytetty suuri määrä piirteitä muista moderneista ohjelmointikielistä, on mielenkiintoista, että rinnakkaisuutta ei ole sallittu (rinnakkaisuuden saa käyttöön JRuby-tulkilla). Tällöin luonnollisestikin on estetty rinnakkaisuuden tuomien ongelmien esiintyminen ohjelmissa, mutta samalla sovellusten käyttökohteista rajautuvat pois kaikki rinnakkaisuudesta ja hajautuksesta hyötyvät sovellusalueet.

Rubyssä on paljon ominaisuuksia, joiden mukana olon voi tulkita joko hyväksi tai huonoksi asiaksi. Kokenut ohjelmistokehittäjä osaa käyttää Rubyn tarjoamia piirteitä tehokkaasti hyödyksi, mutta aloittelevalla ohjelmoijalla voi kielen täysipainoinen hallinta viedä paljon aikaa.

9 Lähteet

  1. Wikipedia, Ruby. [luettu 17.3.2011]
  2. Ruby-lang.org, About. [luettu 17.3.2011]
  3. Wikipedia, YARV. [luettu 17.3.2011]
  4. Sasada, K., YARV: yet another RubyVM: innovating the ruby interpreter. Companion to the 20th annual ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, OOPSLA '05, New York, NY, USA, 2005, ACM, sivut 158--159, URL http://doi.acm.org/10.1145/1094855.1094912.
  5. Wikipedia, Diamond problem. [luettu 17.3.2011]
  6. Wikibooks, Ruby programming, syntax, variables and constants. [luettu 24.3.2011]
  7. Jacob Repp, Alternatives to lexical binding with Ruby. julkaistu 6.1.2008 [luettu 24.3.2011]
  8. Onestepback.org, Variable bindings in Ruby. julkaistu 23.12.2003 [luettu 24.3.2011]
  9. Martinfowler.com, Closure. julkaistu 8.9.2004 [luettu 24.3.2011]
  10. Flanagan, D. ja Matsumoto, Y., The Ruby Programming Language. O'Reilly, 2008.
  11. Robert Sosinski, Understanding Ruby blocks, Procs and Lambdas. julkaistu 21.12.2008 [luettu 31.3.2011]
  12. Troubleshooters.Com, Code corner and Ruby revival: The Ruby_newbie guide to symbols. [luettu 31.3.2011]
  13. François Lamontagne, Ruby is dynamically and strongly typed. julkaistu 9.7.2007 [luettu 31.3.2011]
  14. Werner Schuster, Ruby 1.9 adds Fibers for lightweight concurrency. julkaistu 24.8.2007 [luettu 8.4.2011]
  15. Ilya Grigorik, Concurrency is a myth in Ruby. julkaistu 13.11.2008 [luettu 8.4.2011]
  16. Wikipedia, Global interpreter lock. [luettu 8.4.2011]
  17. Wikipedia, Pipeline software. [luettu 11.4.2011]
  18. Ruby-on-Rails, API. [luettu 11.4.2011]
  19. Roy Fielding, Architectural styles and the design of network-based software architectures. , University of California, 2000 [luettu 11.4.2011]
  20. Wikipedia, Representational state transfer. [luettu 11.4.2011]
  21. Sinatrarb.com [luettu 11.4.2011]
  22. Robert Klemme, Practicing Ruby, Structs inside out, blogi. julkaistu 21.9.2009 [luettu 7.4.2011]
  23. François Lamontagne, Enumerations and Ruby. julkaistu 17.8.2007 [luettu 7.4.2011]
  24. Dave Thomas, Chad Fowler, Andy Hunt, Programming Ruby 1.9, The pragmatic programmers’ guide, Third edition, 2009.
  25. Ruby-doc.org, Classes tutorial. [luettu 21.4.2011]
  26. Ruby-doc.org, Classes. [luettu 21.4.2011]
  27. The Unofficial Ruby usage guide. [luettu 21.4.2011]
  28. Wikipedia, Recursion in computer science. [luettu 28.4.2011]
  29. Wikipedia, Tail call. [luettu 28.4.2011]
Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.