Hlavní navigace

Ruby z rychlíku

8. 2. 2002
Doba čtení: 6 minut

Sdílet

Ruby je mladý, (zatím) interpretovaný, objektově orientovaný programovací jazyk, vytvořený a nezávisle vyvíjený Yukihirem Matsumotem. Interpret jazyka je dobře přenositelný a momentálně existují verze pro různé varianty UNIXu, MS Windows, Mac OS, OS/2 a BeOS. Nejstručnější charakteristika praví, že Ruby=(Perl+Smalltalk)/2.

Základním zdrojem informací o Ruby je www.ruby-lang.org a vynikající on-line kniha Programming Ruby.

Kdy vás může Ruby zaujmout? Zejména pokud splňujete některou z následujících podmínek:

  • stále zkoušíte nové programovací jazyky
  • ještě nemáte svůj oblíbený skriptovací jazyk
  • hledáte v programování krásu konstrukce a potěšení z tvorby
  • máte rádi software s myšlenkou, jasným designem a absencí ad hoc konstrukcí

Článek předpokládá určitou zkušenost a orientaci v běžných pojmech procedurálního a objektového programování. Zabývat se budeme mimo jiné proměnnými, operátory, řídícími strukturami, I/O operacemi a ošetřováním výjimek.

Hello, World!

Přejděme tedy k praxi. Po instalaci můžete (za obvyklých podmínek) ihned spustit interpret v interaktivním režimu pomocí příkazu irb. Irb ihned vyhodnocuje každý zadaný řádek:

irb 0.6.1(99/09/16)
irb(main):001:0> 1+1
2
irb(main):002:0>

Dialog s irb ukončíte stiskem Ctrl+D.

Kód programu vytvořený libovolným editorem lze samozřejmě také uložit do souboru (obyčejně s příponou rb) a spustit příkazem ruby <soubor>.

Obligátní HelloWorld.rb by mohl vypadat například takto:

#!/usr/local/bin/ruby

# Nadefinujeme funkci bez parametrů. Definice končí příkazem end.
def helloWorld_1
  # Funkce puts vypisuje parametry na standardní výstup.
  puts 'Hello, world!'
end

# Zavoláme nadefinovanou funkci.
helloWorld_1

Asi nás nepřekvapí výsledek:

Hello, world!

Podívejme se, co lze zjistit pohledem na kód tohoto jednoduchého příkladu:

  • jak je u interpretovaného kódu zvykem, první řádek začínající na #! určuje cestu k interpretu
  • znak # zároveň označuje komentář, zbytek řádku je interpretem ignorován
  • při programování v Ruby nebudete dvě hodiny ladit kvůli chybějícímu středníku
  • pokud to nevede k nejednoznačnosti, nemusíte používat ani závorky
  • ačkoliv (jak uvidíme) je Ruby opravdu objektový jazyk, nevnucují se nám objekty za každou cenu – příkládek je čistě procedurální
  • kód se vykonává sekvenčně od začátku souboru a neexistuje žádný ekvivalent funkce main z jazyka C

Udělejme malinko složitější HelloWorld2.rb (možná ho plně pochopíte až po dočtení článku):

#!/usr/local/bin/ruby

# Podle toho, zda objekt zadaný parametrem reaguje
# na metodu 'times', funkce zobrazí n krát text, nebo
# přímo zadaný parametr.
def helloWorld_2(n)
  if n.respond_to?(:times)
    n.times { |x| puts "#{x}, Hello, world!" }
  else
    puts n
  end
end

helloWorld_2(3)
helloWorld_2('Konec')

Po spuštění získáme:

0, Hello, world!
1, Hello, world!
2, Hello, world!
Konec

Vidíme, že:

  • definice a volání funkce helloWorld jsou rozšířeny o použití parametru
  • ve funkci se používá řídící konstrukce if/else/end, která bude vysvětlena později
  • ve funkci se využívá volání metod zápisem objekt.metoda
  • volání respond_to? umožňuje zjistit, zda má objekt definovánu zadanou metodu
  • celá čísla mají definovánu metodu times, která volá zadaný blok tolikrát, jaká je hodnota čísla
  • aktuální hodnota iterované proměnné může být předávána do bloku
  • pro ohraničení řetězce lze použít apostrofy (jako v HelloWorld1) nebo uvozovky (jako v HelloWorld2)
  • obsah řetězce v apostrofech se již dále nezpracovává
  • obsah řetězce v uvozovkách se zpracovává (např. v řetězci #{výraz} se výraz vyhodnotí a celý řetězec se nahradí výsledkem)
  • navíc si řekneme, že obsah řetězce v obrácených apostrofech se nahradí standardním výstupem externího příkazu obsaženého v řetězci

a=33
puts '1: abc \n def #{a}'
puts "2: abc \n def #{a}"
puts `date`

Vypíše:

1: abc \n def #{a}
2: abc
 def 33
Fri Feb  1 09:58:58 CET 2002

Proměnné a operátory

Základní fakta:

  • proměnné není třeba deklarovat
  • není třeba se starat o typ proměnné – určuje si ho za běhu interpret (a je možné ho zjistit voláním metody type)
  • není třeba se starat o uvolňování paměti – obstará ho garbage collector
  • v tomto článku se setkáme pouze s lokálními proměnnými a globálními proměnnými – název lokální proměnné začíná malým písmenem, název globální velkým písmenem nebo znakem $

Čísla

Ruby podporuje celá i reálná čísla. Celá čísla odpovídající 32bitovému nebo 64bitovému integeru (podle platformy) jsou interně uložena jako obejkt třídy Fixnum. Větší celá čísla jsou ukládána jako objekt třídy Bignum. Bignum umožňuje pracovat s integery, jejichž velikost je omezena jen velikostí paměti počítače. Protože typy všech proměnných jsou v Ruby určovány až za běhu programu, je i přechod mezi dvěma integerovými typy bezbolestný:

n=8
7.times do                # blok pro opakování je uzavřen mezi 'do' a 'end'
  puts "#{n.type}  #{n}"  # metoda 'type' vrací typ proměnné
  n*=n
end

Vypíše:

Fixnum  8
Fixnum  64
Fixnum  4096
Fixnum  16777216
Bignum  281474976710656
Bignum  79228162514264337593543950336
Bignum  6277101735386680763835789423207666416102355444464034512896

Celá čísla je také možné zapisovat jako binární, oktalová nebo hexadecimální. Dekadické číslo 513 lze zapsat také jako:

  • binární 0b1000000001
  • oktalové 01001
  • hexadecimální 0×201

Protože jsou čísla objekty, ukažme si několik základních metod, které mají definovány:

puts -23.abs          # absolutní hodnota
puts 65.chr           # znak, jehož ASCII kód je roven číslu
puts 3.next           # o jedničku vyšší číslo
puts 0.zero?          # vrací 'true', když je číslo rovno nule
puts 10.remainder(3)  # zbytek po dělení čísla parametrem
puts 15.type          # typ proměnné

Jak jistě tušíte, výsledkem je:

23
A
4
true
1
Fixnum

Stojí za zmínku, že většina operátorů, o kterých bude řeč později, jsou ve skutečnosti metody s upravenou syntaxí volání: a+b můžeme napsat jako a+(b), což je volání metody ‚+‘ objektu ‚a‘ s parametrem ‚b‘.

Reálná čísla jsou uchovávána jako objekty třídy Float, a to ve dvojnásobné přesnosti. Float poskytuje několik základních metod:

puts 3.14.round   # zaokrouhlení podle matematických pravidel
puts 3.14.ceil    # nejbližší větší celé číslo
puts 3.14.floor   # nejbližší menší celé číslo
puts -3.14.round
puts -3.14.ceil
puts -3.14.floor

Nepřekvapí nás výsledek:

3
4
3
-3
-3
-4

Zbytek aritmetických operací je soustředěn v tzv. modulu Math a jejich vysvětlení přesahuje rozsah tohoto článku.

Řetězce

Řetězcové proměnné jsou v Ruby uchovávány v objektech třídy String. Jsou to v podstatě sekvence 8bitových znaků, takže v objektu String lze uchovávat i binární data (například načtená ze souboru). Obvykle se řetězce inicializují pomocí konstant uzavřených v uvozovkách nebo apostrofech a platí stejná pravidla pro vyhodnocování obsahu, jaká byla uvedena výše. Navíc nabízí Ruby pohodlnou cestu k inicializaci dlouhých, několikařádkových řetězců:

s1='Neinterpretuje\nse'
s2="Interpretuje\nse 1+1 = #{1+1}"
s3=<<KONEC
  Tento řetězec může mít několik řádků a končí
  až slovem, které je uvedeno za znaky '<<'.
  (Počítá se do něj i případné odsazení řádků.)
KONEC

puts s1
puts s2
puts s3

Výsledkem je:

Neinterpretuje\nse
Interpretuje
se 1+1 = 2
  Tento řetězec může mít několik řádků a končí
  až slovem, které je uvedeno za znaky '<<'.
  (Počítá se do něj i případné odsazení řádků.)

Protože práce s řetězci je programátorovým denním chlebem, poskytuje String opravdu velké množství metod pro manipulaci s nimi. Uveďme si jen několik jednoduchých příkladů:

puts 'HellO'.downcase         # převod do malých písmen
puts 'hello'.upcase           # převod do velkých písmen
puts 'hello'.length           # délka řetězce
puts 'hello'.include?('el')   # vrací 'true', pokud řetězec obsahuje parametr
puts 'hello'.index('el')      # pozice parametru v řetězci
puts 'hello'.reverse          # obrácený řetězec
puts 'hello'.reverse.reverse  # návratová hodnota je zase objekt, jehož
                              # metodu můžeme volat
puts '   hello   '.strip      # odstranění prázdných znaků ze začátku a konce
puts 'hello'.tr('he','Ha')    # zaměňuje znaky (jako příkaz 'tr' v UNIXu)

Získáme:

hello
HELLO
5
true
1
olleh
hello
hello
Hallo

Navíc můžeme počítat se silnými nástroji pro nahrazování a změny částí řetězců a pro práci s regulárním výrazy, o nichž bude ještě řeč.

Možná jste si již všimli konvence, kdy názvy metod vracejících hodnotu boolean končí otazníkem. Podobně exitují metody, jejichž název končí vykřičníkem. Vykřičník upozorňuje na to, že dochází přímo ke změně samotného objektu. (Normální metody vrací hodnotu, ale stav objektu zůstává nezměněn.)

a='hello'
puts a
puts a.reverse   # jen vrať hodnotu
puts a
puts a.reverse!  # vrať hodnotu a změň objekt
puts a

Vrací:

hello
olleh
hello
olleh
olleh

True, false, nil

Nebudeme-li se snažit o zbytečnou složitost, můžeme říct, že v Ruby jsou definovány tři speciální hodnoty, kterých také může proměnná nabýt:

  • true – označuje pravdu (např. pravdivý výsledek porovnání)
  • false – označuje nepravdu
  • nil – označuje prázdnou proměnnou (například nenalezený znak řetězce)

Použití osvětlí příklad:

skoleni

puts 2<3               # to je pravda
puts 3<2               # to není pravda
puts 'abcd'.index 'r'  # znak v řetězci není - vrací se doslova "nic"

S výsledkem:

true
false
nil

Příště se podíváme na další datové typy a operátory, řídící struktury a také si povíme něco o tom, jak Ruby podporuje mocnou zbraň skriptovacích jazyků – regulární výrazy.