Hlavní navigace

Ruby z rychlíku (2)

15. 2. 2002
Doba čtení: 7 minut

Sdílet

V dnešním pokračování naší seznamovací minisérie se podíváme na další datové typy a operátory, řídící struktury a povíme si také něco o podpoře regulárních výrazů.

Proměnné a operátory (pokračování)

Pole

K uložení jednorozměrného pole slouží objekt třídky Array. Prvkem pole může být libovolný další objekt včetně pole samotného, čímž lze docílit vícerozměrných polí. Array poskytuje automatickou alokaci paměti a kontrolu mezí indexů. Podobně jako u řetězců i pro pole existuje značné množství metod. Nejlépe si vše osvětlíme na příkladech:

def puts_ary ary      # uděláme si funkci na výpis pole
  puts ary.join ', '  # vypiš prvky spojené čárkami a mezerami do řetězce
end

a=['p','o','l','e']   # inicializujeme pole čtyř jednoznakových řetězců

puts a.length         # počet prvků pole (také můžeme použít a.size)
puts a[1]             # prvek s indexem 1 (indexy začínají od nuly)
puts a[-1]            # první prvek od konce pole
puts a.index 'o'      # index prvku, který se rovná 'o'

puts_ary a[2..3]      # pole tvořené prvky s rozsahem indexů 2..3

a[4]=1                # přiřadíme další prvek, pole se automaticky rozšíří
puts_ary a
a.delete_at 4         # zase prvek smažeme (prvek s indexem 4)
puts_ary a

puts_ary a.sort       # získáme seřazené prvky pole
puts_ary a.reverse    # prvky pole v obráceném pořadí

a.push 'x'            # přidání prvku na konec pole
puts_ary a
puts a.pop            # odebrání prvku z konce pole
puts_ary a
a.unshift 'x'         # přidání prvku na začátek pole
puts_ary a
puts a.shift          # odebrání prvku ze začátku pole
puts_ary a

Zobrazí se:

4
o
e
1
l, e
p, o, l, e, 1
p, o, l, e
e, l, o, p
e, l, o, p
p, o, l, e, x
x
p, o, l, e
x, p, o, l, e
x
p, o, l, e

Pomocí metod push, pop, shift a unshift lze realizovat klasické struktury zásobníku a fronty. Tyto metody mění dané pole. U jiných platí stejné pravidlo s vykřičníkem jako u řetězců (pro metodu sort existuje ekvivalent sort! atd.)

Hash

Hash je struktura podobná poli (někdy se mu také říká asociativní pole), ale narozdíl od pole, kde je jako index pevně stanoven integer, může být indexem hashe prakticky libovolný objekt. K ilustraci opět použijeme jednoduchý příklad:

a={'jedna'=>'I',     # hash můžeme inicializovat přiřazení
   'dve'=>'II',      # klíč=>hodnota oddělených čárkami
   'tri'=>'III',
   'ctyri'=>'IV',    # v tomto případě použijeme pro klíč i hodnoty
   'pet'=>'V'        # řetězcové konstanty
}

puts a['dve']        # hodnota s klíčem 'dvě'
puts a['deset']      # hodnota s klíčem 'deset' (není tam, vrací se nil)
a['deset']='X'       # přiřadíme klíči 'deset' hodnotu 'X'
puts a['deset']      # hodnota s klíčem 'deset'
puts a.index 'V'     # klíč hodnoty 'V'
puts a.invert['IV']  # metoda invert vytvoří obrácený hash (hodnota=>klíč)
puts a.length        # počet záznamů v hashi

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

II
nil
X
pet
ctyri
6

Je možné ukládat do hashů pole a naopak, což nabízí značnou flexibilitu pro konstrukci různých datových struktur. Další možnosti práce s poli a hashi uvidíme v sekci o iterátorech.

Operátory

Samozřejmostí jsou operátory pro běžné aritmetické operace a obvyklé relační operátory. Podívejme se na několik dalších, které by nás mohly zajímat:

  • vícenásobné přiřazení umožňuje zápis proměnná1,pro­měnná2=hodnota1,hod­nota2
  • operátor umocňování – 2**3 znamená dvě na třetí
  • operátor porovnání a< ⇒ vrací –1, když je a menší než b, 0, když je a rovno b a 1, když je a větší než b
  • ternární operátor jistě znají programároři v C – ve tvaru podmínka?hodno­ta1:hodnota2 nabývá hodnoty1, pokud je podmínka vyhodnocena jako true, jinak nabývá hodnoty2
  • metoda between? – použitím prakticky operátor, vrací true, pokud se číslo nachází mezi dvěma zadanými parametry
  • operátor + umožňuje spojovat za sebe dva řetězce
  • operátor * umožňuje řetězec několikrát zopakovat

O použití se snadno přesvědčíme na příkladu:

a,b,c=1,2,3           # do a,b a c postupně přiřadíme 1,2 a 3
puts "#{a+b+c}"       # součet a,b a c
puts 2**5             # dvě na pátou
puts 3<=>5            # porovnání 3 a 5
puts 3>4?1:2          # pokud platí 3>4, zobraz 1, jinak 2
puts 4.between?(3,5)  # je 4 mezi 3 a 5?
puts 'a'+'b'          # spojení řetězců
puts 'a'*3            # opakování řetězce

Získáme:

6
32
-1
2
true
ab
aaa

Regulární výrazy

Regulární výrazy jsou mocnou zbraní skriptovacích jazyků a do Ruby jsou elegantně integrovány. Pro naše účely postačí velmi zjednodušená a stručná pravidla:

  • regulární výraz se zapisuje mezi dvě lomítka
  • operátor =~ vrací pozici, na které v řetězci na levé straně začíná podřetězec, který vyhovuje regulárnímu výrazu na pravé straně (pokud takový podřetězec není nalezen, vrací nil)
  • negovanou variantou operátoru =~ je !~ (návratovými hodnotami jsou v tomto případě pouze true a false)

Více osvětlí několik jednoduchých příkladů:

puts 'ahoj'=~/ho/
puts 'ahoj'=~/kk/
puts 'ahoj'!~/ho/
puts 'ahoj'!~/kk/

# složitější regulární výraz pro jednoduchou kontrolu, zda
# má řetězec formát datumu d.m.rrrr
puts '2.2.2002'=~/^[0-9][0-9]?\.[0-9][0-9]?\.[0-9]{4}$/
puts '245.2.2002'=~/^[0-9][0-9]?\.[0-9][0-9]?\.[0-9]{4}$/

Po spuštění uvidíme:

1
nil
false
true
0
nil

Regulární výrazy se používají i jako parametry pro některé metody. Na ukázku si doplníme znalosti metod třídy String:

s='Perl Pike Python Perl Pike Python Perl Pike Python'
puts s
s.sub!(/Perl/,'Ruby')     # nahraď první výskyt slova 'Perl' slovem 'Ruby'
s.gsub!(/Python/,'Ruby')  # nahraď každý výskyt slova 'Python' slovem 'Ruby'
puts s

Vypíše:

Perl Pike Python Perl Pike Python Perl Pike Python
Ruby Pike Ruby Perl Pike Ruby Perl Pike Ruby

Použití regulárních výrazů je daleko více. Navíc jsme se zabývali jen konstantními výrazy, ale regulární výraz je v Ruby objekt jako každý jiný a je možné ho bez problémů vytvářet za běhu programu.

Řídící struktury

Podmínky

Bez podmíněných příkazů a cyklů bychom se v programování daleko nedostali. Už jsem se setkali s podmínkou if. Tělo podmínky je zpracováno pouze tehdy, je-li výraz vyhodnocen jako true.

a=10
if a>15 then puts 'a>15'     # tělo podmínky je oddělené slovem 'then'
elsif a>10 then puts 'a>10'
elsif a>5                    # tělo podmínky je na novém řádku
  puts 'a>5'
else
  puts 'a<=5'
end

Výsledkem je pochopitelně:

a>5

Použití then není povinné, pokud tělo podmínky začíná až na dalším řádku. Obrácenou podmínku lze realizovat pomocí příkazu unless.

Cykly

Základním cyklem při programování je while. Tělo cyklu se provádí tak dlouho, dokud je výraz vyhodnocován jako true.

a=0
b=[]              # prázdné pole
while a<5
  b.push a        # přidáme hodnotu z a na konec pole b
  a+=1
end
puts b.join ', '  # join vrací řetězec s hodnotami pole spojenými parametrem

Program vypíše:

0, 1, 2, 3, 4

Jak příkaz if, tak while lze využít ještě jedním elegantním způsobem:

a=0
b=[]
puts 'O.K.' if a==0
b.push a+=1 while a<5
puts b.join ', '

Výsledkem je:

O.K.
1, 2, 3, 4, 5

Zatímco podmínka if se chová podle očekávání, cyklus poskytuje jiné výsledky než v předchozím případě. Je to způsobeno tím, že již před uložením do pole je proměnná a inkrementována.

Iterátory

Než si ukážeme, co je to iterátor, řekneme si, jak lze v Ruby vytvořit blok kódu. Blok není nic jiného než určitým způsobem ohraničený kousek kódu. Ohraničení lze provést buď složenými závorkami, nebo klíčovými slovy do a end. Sám o sobě nemá blok velký smysl, lze ho však předat jako parametr metodě, která ho může opakovaně volat. Taková metoda se nazývá iterátor.

3.times { print '*' }         # s metodou 'times' jsme se již setkali -
puts                          # provede blok tolikrát, kolik je hodnota čísla,
3.times { |n| print n }       # a hodnotu iterace může do bloku předávat jako
puts                          # parametr
3.upto(5) { |n| print n }     # spolu s metodami 'upto' a 'step' prakticky
puts                          # nahrazuje for cyklus, který v ruby není
3.step(10,2) { |n| print n }
puts

a=['a','b','c']               # malé pole znaků

a.each { print '*' }          # pole má také iterátory - each iteruje přes
puts                                 # všechny prvky
a.each { |n| puts "#{n}, Hello!"  }  # do bloku předává aktuální prvek

b=a.select { |x| x>='b'}  # v tomto případě vrací metoda 'select' jen prvky,
puts b.join ', '          # pro které volaný blok vrací true

Výsledkem bude:

root_podpora

***
012
345
3579
***
a, Hello!
b, Hello!
c, Hello!
b, c

Iterátorů je v Ruby mnohem více a snadno si lze také vytvořit vlastní. Největší využití najdou zřejmě při nahrazování běžných cyklů, a zejména pro práci s prvky kontejnerů – tj. s poli, hashi a dalšími.


Příště se podíváme na to, jak se v Ruby pracuje se soubory a sokety, na ošetřování výjimek, standardní knihovnu a chybět nebude ani jeden větší demonstrační příklad.

Byl pro vás článek přínosný?