Samouczek Ruby

Poniższy samouczek zawiera wiedzę niezbędną do rozpoczęcia przygody z programowaniem w Rubym.

Ruby to język programowania ogólnego zastosowania, który ze względu na znakomity framework Ruby on Rails (RoR) często wykorzystywany jest do tworzenia serwisów internetowych.

W przypadku znalezienia błędu, czy niejasności lub jeśli masz jakieś pytania mailuj na ruby@davinci.mimuw.edu.pl - na pewno pomożemy!

Autorem tego samouczka jest Paweł Jankowski.

Prolog

Samouczek, który właśnie rozpoczynasz ma na celu wprowadzić Cię w świat programowania (jeśli wcześniej tego nie robiłeś/aś) i języka Ruby. Na wstępie pragniemy zaznaczyć, iż nie jest to kompendium wiedzy na ten temat, ani nawet kurs, po którym staniesz się znakomitym Rubyistą. Potraktuj ten tutorial raczej jako punkt wyjścia i zachętę do poznania tajników Ruby’iego i programowania jako takiego.

Jeśli podczas przechodzenia przez kolejny rozdział nie będziesz czegoś rozumieć, albo poczujesz niedosyt wiedzy i umiejętności, proponujemy wyjrzeć poza ramy tego samouczka i poszukać informacji w innych źródłach:

Sporą część tekstu stanowią fragmenty kodu. Gorąco zachęcamy do kopiowania/przerabiania/uruchamiania przykładowych programów i komend - to naprawdę świetny sposób nauki.

Różne nazwy i napisy, które znajdziesz w kodzie będą zapisane w języku angielskim. Istnieje pewna konwencja programistyczna (którą popiera autor tego samouczka ;)), która mówi, że tak należy kodować. Dzięki niej kod staje się "międzynarodowy", czytelny dla wszystkich. Nigdy nie wiadomo kto zajrzy do Twojego programu ;) Jeśli zostaniesz profesjonalnym Rubyistą, 99% kodu na który trafisz będzie utrzymany w tej konwencji, więc dobrze oswajać się z nią, od początku przygody z programowaniem.

So... Are you ready? ;)

 

Dla ciekawych świata:
Słowo Rubyist znajdziemy w słowniku ;)

Instalacja

Na wstępie warto zauważyć, że Ruby dostępny jest w kilku wersjach. Istnieją między nimi znaczące różnice. Zakres materiału w tym samouczku nie zawiera jednak zagadnień wymagających rozróżnienia na wersje, więc śmiało możesz instalować dowolne wydanie kompatybilne z Twoim systemem operacyjnym (oczywiście polecamy najnowszą wersję). Więcej na ten temat (i na każdy inny związany z Rubym) możesz poczytać na oficjalnej stronie tego języka. Jeśli zwyciężysz nasz samouczek, a nie on Ciebie, ten adres przyda Ci się jeszcze nie raz ;)

Instalacja Ruby’ego jest bardzo prosta ze względu na narzędzia automatyzujące ten proces (pod warunkiem, że uważnie czyta się instrukcje ;)):

  • Jeśli posiadasz system operacyjny z rodziny Windows, polecamy program RubyInstaller.
  • W przypadku systemu z rodziny Linux wystarczy, że użyjesz domyślnego dla Twojej dystrybucji menadżera pakietów (np. polecenie sudo apt-get install ruby dla systemów typu Debian/Ubuntu).
  • Jeżeli używasz OS X - masz już Ruby’iego “na pokładzie”.

Więcej porad na temat instalowania Ruby’iego znajdziesz tutaj.

Podstawy

Po instalacji Ruby’iego pora zobaczyć “z czym to się je”.

Istnieją dwa podstawowe tryby pracy z Rubym. Możemy napisać program w postaci pliku tekstowego, a następnie uruchomić go za pomocą interpretera (czyli specjalnego programu, który analizuje kod i wykonuje zawarte w nim instrukcje) lub interaktywnie wpisywać polecenia w konsoli.

Pierwszy program

Naukę zaczniemy bez ogródek - napiszemy w pełni działający program. Co musimy zrobić? Przede wszystkim musimy stworzyć pusty plik tekstowy i napisać w nim kod programu.

Potrzebny nam będzie to tego edytor tekstu. Teoretycznie wystarczy do tego zwykły notatnik, który znajdziemy w każdym systemie operacyjnym. Są jednak specjalne edytory tekstu, które znacznie ułatwiają programowanie - kolorują składnie, formatują wcięcia, a niektóre nawet wyłapują błędy. Istnieje kilkadziesiąt, jeśli nie kilkaset takich edytorów - każdy programista ma swój ulubiony i ciężko wskazać najlepszy. Do nauki Ruby’iego ze względu na prostotę działania, wieloplatformowość oraz otwartą licencję polecamy program Geany.

Uzbrojeni w odpowiednie narzędzia możemy brać się do kodowania! Na początku stwórz pusty plik pod nazwą z rozszerzeniem .rb, (np. first_program.rb). Napisz w nim swój pierwszy program (póki co nie przejmuj się tym, co oznaczają poszczególne fragmenty kodu):

puts "Hello world!"

Bum! Udało się! Twój pierwszy program w Rubym jest gotowy!

Teraz uruchom swój program za pomocą specjalnego programu - interpretera. Aby to zrobić musisz wpisać w konsoli/terminalu (na systemach typu Windows program cmd (Command Prompt)) polecenie ruby ścieżka-pliku-z-programem. Np:

ruby C:\programy\first_program.rb

Jeżeli używasz Geany, wystarczy, że wciśniesz F5 z otwartym plikiem - edytor uruchomi odpowiednie polecenie za Ciebie!

Jeśli wszystko poszło tak jak trzeba powinieneś/powinnaś widzieć efekty wykonania Twojego pierwszego programu - wyświetlony napis Hello world!. Może spróbujesz napisać program, który wypisze coś innego?

Interaktywny Ruby (Interactive Ruby – IRB)

Czasami, gdy chcemy np. policzyć jakieś wyrażenie arytmetyczne, albo szybko sprawdzić jak w praktyce działa jakaś metoda, wygodniejsze od pisania skryptu może być IRB - narzędzie, które interaktywnie (“na żywo”) wykonuje wpisany kod.

Aby uruchomić IRB wystarczy wpisać w konsoli polecenie irb. Jeśli używasz systemu typu Windows i zainstalowałeś/aś Ruby’iego za pomocą RubyInstallera, możesz uruchomić IRB wybierając w menu program Interactive Ruby.

Twoim oczom powinien ukazać się następujący napis:

irb(main):001:0>

Oznacza on, że IRB jest gotowy do przetworzenia polecenia. Wpiszmy więc proste wyrażenie arytmetyczne (np. 2+2) i zatwierdźmy je enterem. Co otrzymujemy w wyniku?

irb(main):001:0> 2+2
=> 4
irb(main):002:0>

Oczywiście czwórkę. Zauważmy jednak, że jest ona poprzedzona “strzałką”. W ten sposób Ruby zwraca wynik ostatniego wyrażenia jakie przetworzył. Co więcej, poniżej otrzymaliśmy linijkę bardzo podobną do tej, która wyświetliła się po uruchomieniu IRB - mam nadzieję, że domyślasz się co to oznacza ;) Wpiszmy teraz kod, który wcześniej wpisaliśmy w pliku z rozszerzeniem .rb:

irb(main):001:0> 2+2
=> 4
irb(main):002:0> puts "Hello world!"
Hello world!
=> nil
irb(main):003:0>

Zinterpretujmy teraz to, co zrobił interpreter. Najpierw wykonał polecenie puts, które wyświetla podany w cudzysłowach tekst i przechodzi do następnej linii (o tym dlaczego w cudzysłowach - dowiesz się w następnym rozdziale). Następnie zwrócił wartość ostatniego przetworzonego wyrażenia (Wartość wyrażenia puts “cokolwiek” zawsze wynosi nil. Nil oznacza nic, pusty - o tym też dowiemy się więcej w następnym rozdziale). Na końcu widzimy linijkę oznaczającą, że możemy wpisać kolejne komendy, ale na to przyjdzie jeszcze czas. Wpiszmy exit i po wciśnięciu klawisza enter, wyjdźmy z IRB.

Kiedy IRB, a kiedy praca z plikami?

(Prawie) Każdy program można napisać zarówno w IRB jak i w postaci plików tekstowych. Wybór odpowiedniego trybu może jednak oszczędzić sporo pracy i nerwów.

W trybie interaktywnym wygodnie jest sprawdzić jak działa konkretny fragment kodu, policzyć jakieś wyrażenia arytmetyczne, czy też wykonać kilka prostych operacji, których nie zamierzamy powtarzać w przyszłości. Pracując w ten sposób musimy jednak pamiętać, że to co robimy nie zostanie zapisane po wyłączeniu IRB. Kolejną wadą tego podejścia jest fakt, że gdy przy wpisywaniu wielolinijkowego wyrażenia popełnimy literówkę - będziemy musieli wpisać je od nowa. Jeśli więc nie wykonujesz czynności wspomnianych na początku tego akapitu - dla własnej wygody pracuj na plikach.

Proste typy danych

Programista podczas tworzenia programów musi operować na różnych typach danych - liczbach, napisach i nie tylko... W tym rozdziale poznamy wszystkie podstawowe typy danych w Rubym, jednak najpierw - zapoznamy się z pojęciem zmiennej.

Zmienne

Tworząc program, często chcemy użyć tych samych danych w kilku miejscach programu. Wpisywanie ich za każdym razem od nowa jest dosyć uciążliwe, dlatego warto przypisać je na zmienną:

irb(main):001:0> my_number = 54684859685469496879168591685
=> 54684859685469496879168591685

Aby przypisać jakiś obiekt (np. napis) na zmienną, należy wpisać jej nazwę (dowolny ciąg znaków (małych i wielkich liter, cyfr i podkreślników), przy czym pierwszy znak musi być małą literą lub podkreślnikiem), znak równości i przypisywany obiekt. W powyższym przykładzie przypisaliśmy liczbę 5468485968546.9496879168591685 na zmienną o nazwie my_number. Dzięki temu nie musimy jej wpisywać za każdym razem od nowa, wystarczy użyć nazwy zmiennej, na którą została przypisana:

irb(main):001:0> my_number = 54684859685469496879168591685
=> 54684859685469496879168591685
irb(main):002:0> my_number+123456789
=> 54684859685469496879292048474
irb(main):003:0> my_number*3
=> 164054579056408490637505775055
irb(main):004:0>my_number**2 # Exponentiation
=> 2990433878819487041144980098826114485630894125386251139225

Jak widzisz, Ruby sprawuje się całkiem nieźle jako kalkulator ;) W ostatnim wpisanym poleceniu możesz zauważyć krzyżyk (hash). Służy on do umieszczania komentarzy w kodzie programu (interpreter Ruby’iego pomija napisy/kod, które znajdują się po prawej stronie krzyżyka, aż do następnej linijki). Komentarze są bardzo cenne, kiedy pracujesz nad programem w zespole lub gdy zajrzysz do swojego kodu po długim czasie ;)

Czasem możemy chcieć zmienić wartość naszej zmiennej. Aby to zrobić wystarczy po prostu przypisać na naszą zmienną nowy obiekt. Nie musi być takiego samego typu jak poprzedni! Np. jeśli zmienna my_number była liczbą, możemy teraz przypisać napis:

irb(main):001:0> my_number = 5
=> 5
irb(main):002:0> my_number = 'seven'
=> 'seven'

Zauważ, że napisy w Rubym (jak w większości innych języków programowania) zapisujemy między dwoma apostrofami (lub w cudzysłowiu, ale o tym trochę później). Jeśli o tym zapomnimy interpreter "pomyśli", że chodzi nam o zmienną, a nie napis ;)

Proste typy danych

Wiesz już jak przypisać coś na zmienną. Czas dowiedzieć się co można na nią przypisać ;)

Liczby (Fixnums & Floats)
Na tym typie danych operowaliśmy do tej pory najwięcej, więc już wiesz jak działa. Warto jednak wspomnieć, że w Rubym mamy do wyboru mamy dwa typy reprezentujące liczby: całkowite i zmiennoprzecinkowe. Te pierwsze zapisujemy bardzo intuicyjnie (np. 5, 123, czy -10). Te drugie też, ale musimy pamiętać, że część ułamkową oddzielamy od części całkowitej kropką, a nie przecinkiem (np. 12.34, 17.5, -0.001). Czym się różnią w praktyce te dwa typy? Otóż wynik działania, w którym występują same liczby całkowite zawsze będzie liczbą całkowitą (podłogą wyniku)! 

irb(main):001:0> 5/2
=> 2

Na pierwszy rzut oka wydaje się to bardzo nieintuicyjne, ale za jakiś czas zobaczysz sytuacje, w których ten mechanizm będzie bardzo pomocny. Aczkolwiek nie zawsze tak jest (np. gdy chcesz po prostu dowiedzieć się ile to jest pięć podzielić na dwa ;)). Co więc musimy zrobić, aby otrzymać poprawny matematycznie wynik? Zapisać co najmniej jedną z tych liczb jako liczbę zmiennoprzecinkową (czyli dopisać .0):

irb(main):001:0> 5/2
=> 2
irb(main):002:0> 5/2.0
=> 2.5

Co ciekawe (i unikatowe dla Ruby’iego), dla czytelności zapisu możemy oddzielać cyfry podkreślnikami. Poprawny więc będzie zapis 1_000_000_000 (który jest bardziej czytelny niż 1000000000).

Napisy (Strings)
Z napisem też już się raz spotkaliśmy. Zapisujemy je za pomocą apostrofów lub w cudzysłowie, np. "Short text", 'Short text, but longer than the previous one'. Czym różnią się te formy zapisu? Tekst między apostrofami zostanie zinterpretowany dokładnie tak jak został wpisany. Jeżeli zaś napis zostanie umieszczony w cudzysłowie, interpreter zmieni nieco jego wygląd, jeśli trafi na znak ucieczki (odwrócony ukośnik, np. fragment napisu “\n” zostanie zinterpretowany jako znak nowej linii, a “\t” jako tabulator) lub string interpolation, czyli “wstrzyknięcie” zmiennej, lub nawet całego wyrażenia w napis, za pomocą krzyżyka i nawiasów klamrowych, np. "I'm #{2016-1994} y.o.". Zobaczmy jak to wygląda w praktyce (zauważ, że pierwsze okienko symbolizuje plik tekstowy (co zaznaczone jest odpowiednim komentarzem), a drugie konsolę):

#File: Strings.rb
puts 'Hey'
puts "Hey"
puts 'Do not look down!\n\n\n\n\n\n\n\n\n\n\n\nOh, you!'
puts "Do not look down!\n\n\n\n\n\n\n\n\n\n\n\nOh, you!"
puts 'I am #{2016-1994} y.o.'
puts "I am #{2016-1994} y.o."
jan@Stacja-Linux ~/Pulpit $ ruby Strings.rb 
Hey
Hey
Do not look down!\n\n\n\n\n\n\n\n\n\n\n\nOh, you!
Do not look down!
 
 
 
 
 
 
 
 
 
 
 
Oh, you!
I’m #{2016-1994} y.o.
I’m 22 y.o.

Zauważ, że w cudzysłowie można bez obaw zapisać apostrof i odwrotnie. Jeśli zaś, zapiszemy cudzysłów wewnątrz napisu utworzonego cudzysłowem zakończy się to błędem, gdyż interpreter źle “określi” miejsce, w którym chcemy zakończyć napis:

irb(main):001:0> puts "She said "Hi""
SyntaxError: (irb):1: syntax error, unexpected tCONSTANT, expecting end-of-input
puts "She said "Hi""
                  ^
	from /home/jan/.rbenv/versions/2.1.7/bin/irb:11:in `<main>'

Możemy to jednak przezwyciężyć znakami ucieczki:

irb(main):002:0> puts "She said \"Hi\"" 
She said "Hi"
=> nil

Typ logiczny (Boolean)
Czasem zdarza się, że chcemy zapisać w programie dane, które mogą przyjąć tylko dwa stany (np. włączony/wyłączony, prawda/fałsz, krajowy/zagraniczny). Służy do tego specjalny, logiczny typ danych. Ma tylko dwie możliwe wartości: true i false. Mamy z nimi do czynienia na przykład przy porównaniach:

irb(main):001:0> 5>1
=> true
irb(main):002:0> 3<2.999
=> false
irb(main):003:0> 17>=10+2+5
=> true
irb(main):004:0> 2+2==2*2 # is L equal to R?
=> true
irb(main):004:0> 3+3+3!=3*3 # is L not equal to R?
=> false

Nil
Ciężko sobie wyobrazić typ danych, który byłby prostszy niż boolean, ale takowy istnieje. Istnieje, gdy coś nie istnieje ;). W poprzednim rozdziale używaliśmy polecenia puts do wypisywania napisów w IRB. IRB po wykonaniu polecenia zawsze zwraca wartość ostatniego przetworzonego wyrażenia. Ale jaka miałaby być wartość wyrażenia wypisującego tekst? W Rubym jest to nil, czyli pustka, nieokreśloność, nic...

Metody

Znamy już kilka podstawowych typów danych, więc czas dowiedzieć się co można z nimi robić. A można bardzo dużo!

Co to jest metoda?

Metoda to wydzielona część kodu, będąca zbiorem wyrażeń, który zwraca pewną wartość. Od razu warto zauważyć, że metod możesz używać wielokrotnie. Zamiast zapisywać pewien ciąg operacji kilka(set) razy w kodzie, lepiej wydzielić je w metodę. Co więcej, metody mogą przyjmować pewne argumenty (obiekty) i zwracać wartość w zależności od nich.

Zapewne pamiętasz “magiczną linijkę” wypisującą podany tekst: puts “Your text”. Tak naprawdę ta linijka to wywołanie metody puts z argumentem “Your text”. Poznajmy nową metodę:

irb(main):001:0> "What is this?".include? "is"
=> true
irb(main):002:0> "What’s that?".include? "is"
=> false

Metoda include? sprawdza czy jeden podany napis zawiera drugi. Zauważmy, że sposób wywołania tej metody (argument, kropka, nazwa_metody, (argument(y))) różni się od tego w metodzie puts (nazwa_metody, (argumenty)). Na szczęście, pomijając operatory (o których później) są to jedyne “sposoby” wywoływania metod w Rubym. Jedyne, z dokładnością do nawiasów. Argumenty metody mogą być ujęte w nawiasy (czasem muszą, ale ten wątek zostawimy na inną chwilę) lub nie. Wszystkie poniższe sposoby zapisu są poprawne:

#File: Methods.rb
puts ”Hey”
puts(”Hey”)
"abcdefg".include? "a"
"abcdefg".include?("a")

Definiowanie własnych metod

Często zdarza się, że język programowania w którym działamy nie posiada metody, której potrzebujemy (szczególnie, gdy jest osobliwa ;)). Możemy jednak zdefiniować własną:

#File: My_own_method.rb
def about_me(name, fav_book, fav_actor)
  puts "My name is #{name}"
  puts "My favourite book is \"#{fav_book}\""
  puts "My favourite actor is #{fav_actor}"
end
 
about_me "Janek", "Agile Web Development with Rails 4", "Nicolas Cage"
jan@Stacja-Linux ~/Pulpit $ ruby My_own_method.rb 
My name is Janek
My favourite book is "Agile Web Development with Rails 4"
My favourite actor is Nicolas Cage

Zastanówmy się teraz, co zostało zakodowane w pliku My_own_method.rb ;) Najpierw widzimy definicję metody. Ogólna składnia (czyli sposób zapisu) to:

def nazwa_metody(argumenty)
  CIAŁO METODY
end

Zaczynamy od słowa kluczowego def, nazwy metody oraz jej argumentów w nawiasie lub bez (oczywiście możemy też zdefiniować funkcję bez argumentów). W następnych liniach zapisujemy “to” co metoda ma robić. W ostatniej linijce kończymy definicję słowem kluczowym end.

Zauważ, że ciało metody jest wcięte w stosunku do pierwszej i ostatniej linii definicji. Interpreter Ruby'iego nie zwraca uwagi na wcięcia (w przciwieństwie np. do Pythona), ale dzięki nim kod jest bardziej czytelny. Istotne są jednak znaki końca linii ("entery").

Każda metoda zwraca ostatnie przetworzone w niej wyrażenie (about_me zwraca nil). Poniżej pewien dający do myślenia przykład:

#File: My_own_methods.rb
def potential_prime(n)
  2**n-1
  puts “Thanks for using my method!”
end
 
def potential_prime2(n)
  puts “Thanks for using my method!”
  2**n-1
end
 
puts potential_prime 5
puts potential_prime2 5
jan@Stacja-Linux ~/Pulpit $ ruby My_own_methods.rb
Thanks for using my method!
 
Thanks for using my method!
31

Metoda potential_prime oblicza wyrażenie 2**n-1, po czym wyświetla komunikat tekstowy. Ostatnim przetworzonym wyrażeniem jest wypisanie napisu, przez co metoda zwraca wartość nil (stąd pusta linijka po uruchomieniu programu). Metoda potential_prime2 robi to samo, ale w odwrotnej kolejności, dzięki czemu zwracana jest wartość o którą nam chodzi.

Argumenty metody w Rubym mogą mieć wartość domyślną (czyli taką, która zostanie wykorzystana, jeśli w wywołaniu metody nie będzie podanej innej wartości). Aby ją określić dopisz znak równości i wartość po nazwie argumentu:

#File: Default_parameter.rb
def greetings(name="Anonymous")
  puts name
end
 
greetings
greetings 'Janek'
jan@Stacja-Linux ~/Pulpit $ ruby Default_parameter.rb
Anonymous
Janek

Operatory

Operatory to jedno- lub wieloargumentowe konstrukcje zwracające pewną wartość. W Rubym operatory to tak naprawdę wywołania metod, ale ich specjalna składnia sprawia, że kod wygląda przejrzyściej. Poniżej krótki przegląd najważniejszych operatorów.

Operatory arytmetyczne
Spotkałeś/aś się z nimi już wcześniej. Jedyna “nowość” to operator %, który zwraca resztę z dzielenia. Tak przy okazji - w wyrażeniach arytmetycznych i logicznych możesz używać nawiasów ;):

#File: Arithmetic_operators.rb
puts "5+2 = #{5+2}"
puts "5-2 = #{5-2}"
puts "5*2 = #{5*2}"
puts "5/2 = #{5/2} r. #{5%2}"
puts "5/2 = #{5.0/2.0}"
puts "5**2 = #{5**2}"
puts "(5+6)**2*(3+4) = #{(5+6)**2*(3+4)}"
jan@Stacja-Linux ~/Pulpit $ ruby Arithmetic_operators.rb
5+2 = 7
5-2 = 3
5*2 = 10
5/2 = 2 r. 1
5/2 = 2.5
5**2 = 25
(5+6)**2*(3+4) = 847

Operatory porównań
Ten typ operatorów też nie jest Ci już obcy. Zaliczamy do niego m. in. operatory: równe z (==), różne od (!=), większe niż (>), mniejsze niż (<), większe równe (>=), mniejsze równe (<=):

#File: Comparision_operators.rb
puts "1==1 #{1==1}"
puts "1!=1 #{1!=1}"
puts "3>2 #{3>2}"
puts "2>3 #{2>3}"
puts "3>=3 #{3>=3}"
puts "3<=2 #{3<=2}"
jan@Stacja-Linux ~/Pulpit $ ruby Comparision_operators.rb
1==1 true
1!=1 false
3>2 true
2>3 false
3>=3 true
3<=2 false

Operatory logiczne
Do operatorów logicznych zaliczamy m. in. and, or oraz not:

#File: Logical_operators.rb
puts "true and true:   #{true and true}"
puts "true and false:   #{true and false}"
puts "false or false:   #{false or false}"
puts "false or true:   #{false or true}"
puts "not false:   #{not false}"
puts "not(true and false):   #{not(true and false)}"
puts "not((2>4) or (5==2+2)):   #{not((2>4) or (5==2+2))}"
jan@Stacja-Linux ~/Pulpit $ ruby Logical_operators.rb
true and true:   true
true and false:   false
false or false:   false
false or true:   true
not false:   true
not(true and false):   true
not((2>4) or (5==2+2)):   true

Przegląd najważniejszych metod (i operatorów) dla typów liczbowych i tekstowego.

Liczby
Pomijamy operatory, których działanie dla liczb już analizowaliśmy.

Metoda Opis
to_i “Przerabia” podany obiekt na liczbę całkowitą.
to_f “Przerabia” podany obiekt na liczbę zmiennoprzecinkową.
abs Zwraca wartość absolutną podanej liczby.
floor Zwraca podłogę podanej liczby.
ceil Zwraca sufit podanej liczby.
round(ndigits) Zwraca zaokrąglenie podanej liczby do ndigits miejsc po przecinku. Argument ndigits może być ujemny! Domyślnie ndigits=0.

Poniżej praktyczne spojrzenie na powyższe metody:

irb(main):001:0> "Text".to_i
=> 0
irb(main):002:0> "14".to_i
=> 14
irb(main):003:0> 123.456.to_i
=> 123
irb(main):004:0> 1.to_i
=> 1
irb(main):005:0> 1.to_f
=> 1.0
irb(main):006:0> -12.to_f
=> -12.0
irb(main):007:0> -12.abs
=> 12
irb(main):008:0> 3.14.abs
=> 3.14
irb(main):009:0> 3.14.floor
=> 3
irb(main):010:0> 3.14.ceil
=> 4
irb(main):011:0> 3.14159.round
=> 3
irb(main):012:0> 3.14159.round 4
=> 3.1416
irb(main):013:0> 3.14159.round 2
=> 3.14
irb(main):014:0> -123.14159.round 
=> -123
irb(main):015:0> -123.14159.round 2
=> -123.14
irb(main):016:0> -123.14159.round -1
=> -120
irb(main):017:0> -123.14159.round -2
=> -100

Napisy

Metoda Opis
to_s “Przerabia” podany obiekt na napis.
+ Łączy podane napisy.
* Powiela podany napis.
capitalize Zwraca kopię napisu, zamieniając pierwszą literę na wielką literę. 
downcase Zwraca kopię napisu, zamieniając wszystkie litery na małe litery.
upcase Zwraca kopię napisu, zamieniając wszystkie litery na wielkie litery.
count(other_str) Zwraca ilość wystąpienia napisu other_str w napisie.
gsub(pattern, replacement) Zwraca kopię napisu z zamienionymi wystąpieniami pattern na replacement.
reverse Zwraca odwrócony napis.
chomp Usuwa białe znaki z końca napisu.

Poniżej powyższe metody w przykładach:

irb(main):001:0> "abc".to_s
=> "abc"
irb(main):002:0> -12.5.to_s
=> "-12.5"
irb(main):003:0> "Nicolas "+"Cage" 
=> "Nicolas Cage"
irb(main):004:0> "Nicolas Cage "*10
=> "Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage Nicolas Cage "
irb(main):005:0> "university of Warsaw".capitalize
=> "University of warsaw"
irb(main):006:0> "Hi!".capitalize
=> "Hi!"
irb(main):007:0> "PoKeMoN!".downcase
=> "pokemon!"
irb(main):008:0> "PoKeMoN!".upcase
=> "POKEMON!"
irb(main):009:0> "An Anaconda".count("a")
=> 2
irb(main):010:0> "An Anaconda".count("n")
=> 3
irb(main):011:0> "An Anaconda".count("r")
=> 0
irb(main):011:0> "I really like Python, because Python is simple".gsub("Python", "Ruby")
=> "I really like Ruby, because Ruby is simple"
irb(main):012:0> "I really like Python, because Python is simple".gsub("C++", "Java")
=> "I really like Python, because Python is simple"
irb(main):013:0> "stressed".reverse
=> "desserts"
irb(main):013:0> "new line\n".chomp
=> "new line"

Metody “uniwersalne”

Istnieją metody, których działanie nie jest ograniczone do jednego typu danych. Poznaliśmy już jedną taką metodę - puts. Za jej pomocą możemy wypisać zarówno napis jak i liczbę (a nawet typ logiczny i nil). W analogiczny sposób działają też metody print i class (i wiele, wiele innych). Print robi to samo co puts, z dokładnością do znaku nowej linii, którego w tym przypadku nie ma. Metoda class zwraca nazwę typu obiektu, który został jej przekazany:

#File: Print_class.rbputs 10.class
puts 10.0.class
puts "10”.class
print 20.class
print 20.0.class
print "20”.class
jan@Stacja-Linux ~/Pulpit $ ruby Print_class.rb 
Fixnum
Float
String
FixnumFloatString

Wywoływanie metody, a nawiasy

Wspominaliśmy wcześniej, że argumenty metody mogą być objęte nawiasem (nawet jeśli ich liczba to zero ;)) lub nie:

irb(main):001:0> "123".to_i
=> 123
irb(main):002:0> "234".to_i()
=> 234

Zazwyczaj nie ma to znaczenia (poza przejrzystością kodu), ale jest jedna sytuacja, kiedy nawiasy są niezbędne - kiedy chcemy wywołać kolejną metodę na wyniku poprzedniej:

irb(main):001:0> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".count("a").round(-1)
=> 40
irb(main):002:0> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".count "a".round(-1)
NoMethodError: undefined method `round' for "a":String
	from (irb):2
	from /home/jan/.rbenv/versions/2.1.7/bin/irb:11:in `<main>'

Pierwsze wyrażenie jest poprawne - najpierw obliczana jest ilość wystąpień litery ”a” w napisie, po czym zwracana liczba zaokrąglana jest do pełnych dziesiątek. Drugie wyrażenie jest błędne, ponieważ interpreter Ruby’iego “odczytuje” je tak, jakby wyrażenie “a”.round(-1) miało być argumentem dla metody count. Metoda round nie jest jednak zdefiniowana dla typu String, przez co następuje błąd.

Dla ciekawych świata:
Trochę więcej o wcięciach i ogólnie składni Ruby'iego.
Kompletny przegląd metod dla napisów, liczb całkowitych i liczb zmiennoprzecinkowych (zauważ, że w tekście samouczka metody dla liczb zostały ujęte w jedną tabelkę, gdyż te wymienione działają dla obu typów).

Dla bardzo ciekawych świata:
O wzorze w funkcji potential_prime.

Wyrażenia warunkowe

W poniższym rozdziale nauczysz się używać jednych z dwu (obok pętli) najważniejszych konstrukcji w programowaniu - wyrażeń warunkowych. Najpierw jednak poznamy pewne metody, dzięki którym nasze programy staną się interaktywne - dzięki temu łatwiej będzie nam się uczyć kolejnych zagadnień.

Interaktywność

Praktycznie każdy zaawansowany program oferuje użytkownikowi jakiś stopień interaktywności. Inaczej mówiąc, program komunikuje się z użytkownikiem i vice versa. Wiemy już jak w Rubym program może “powiedzieć” coś użytkownikowi - np. za pomocą metod puts i print. Pora dowiedzieć się jak użytkownik programu może coś mu “powiedzieć”. Służy do tego metoda gets:

#File: Floor.rb
puts "Type a number:"
n = gets
puts "Floor:"
puts n.to_i
jan@Stacja-Linux ~/Pulpit $ ruby Integer.rb
Type a number:

Przeanalizujmy powyższy program. Najpierw wyświetlany jest napis “Type a number:”. W następnej linijce, na zmienną n przypisywany jest wynik metody gets. Ale co jest jej wynikiem? To co wpisze użytkownik ;) Wpiszmy więc coś (np. 15.6) i wciśnijmy enter:

jan@Stacja-Linux ~/Pulpit $ ruby Integer.rb
Type a number:
15.6
Floor:
15

Otrzymaliśmy podłogę podanej liczby, ale jak to się stało? Po przypisaniu napisu "15.6" na zmienną n, wyświetlony został napis "Floor:". Następnie wyświetlona została zmienna n, przekształcona na typ liczby całkowitej (Przekształcenie to (zwane rzutowaniem lub konwersją) jest tu niezbędne, gdyż wynikiem metody gets jest zawsze napis). Zauważ, że powyższy program można napisać krócej i bez użycia zmiennych (ale chyba kosztem czytelności ;)):

#File: Floor2.rb
puts “Type a number:”
puts “Floor\n #{gets.to_i}”

Wyrażenie warunkowe if

Do tej pory pisaliśmy programy, które robiły to samo, niezależnie od otrzymanych danych (jedynymi możliwymi urozmaiceniami były błędy, gdy przekazane dane były niewłaściwe ;)). Pora to zmienić!

Przeanalizujmy wyrażenie if. Składnia jest następująca:

if (WARUNEK)
  INSTRUKCJE
elsif (INNY_WARUNEK)
  INNE_INSTRUKCJE
else
  JESZCZE_INNE_INSTRUKCJE
end

To co dzieje się po wykonaniu kodu zgodnego z powyższym schematem jest bardzo intuicyjne. Jeśli spełniony (tj. o wartości logicznej true) jest WARUNEK przy słowie kluczowym if, wykonają się INSTRUKCJE poniżej, aż do kolejnego słowa kluczowego (w tym przypadku elsif). Jeśli zaś WARUNEK ma wartość false, INSTRUKCJE te nie będą wykonane i nastąpi ewaluacja WARUNKU przy słowie elsif. Jeśli INNY_WARUNEK jest spełniony wykonają się INNE_INSTRUKCJE poniżej, aż do kolejnego słowa kluczowego (w tym przypadku else), w przeciwnym wypadku wykona się kod między słowami else i end.

Ilość warunków elsif jest dowolna, więc poprawne będą również wyrażenia o schemacie jak poniżej...:

if (WARUNEK_1)
  INSTRUKCJE_1
elsif (WARUNEK_2)
  INSTRUKCJE_2
elsif (WARUNEK_3)
  INSTRUKCJE_3
elsif (WARUNEK_4)
  INSTRUKCJE_4
else
  INSTRUKCJE_5
end

... i poniżej:
if (WARUNEK_1)
  INSTRUKCJE_1
else
  INSTRUKCJE_5
end

Co więcej, słowo kluczowe else, które “zbiera pozostałe przypadki” również nie jest obowiązkowe. Warunki nie muszą zaś być objęte w nawiasy (ale czasem poprawia to czytelność kodu). Poprawny będzie więc zapis:

if WARUNEK
  INSTRUKCJE
end

Spróbujmy teraz napisać i przeanalizować program, który sprawdzi, czy podana liczba jest podzielna przez 13:

 

#File: Divisibility.rb
puts "Type a number:"
n = gets.to_f
if n%13==0
  puts "#{n} is divisible by 13"
end 

Powyższy program prosi użytkownika o liczbę. Jeśli podana liczba jest podzielna przez 13, program wypisuje stosowny komunikat. W przeciwnym przypadku… nie robi nic. Spróbujmy przerobić ten program tak, aby był bardziej user-friendly:

#File: Divisibility2.rb
puts "Type a number:"
n = gets.to_f
if n%13==0
  puts "#{n} is divisible by 13"
else
  puts "#{n} is NOT divisible by 13"
end 

Teraz jeśli liczba nie będzie podzielna przez 13, użytkownik również otrzyma stosowną wiadomość.

Poniżej przykład programu (liczącego ilość miejsc zerowych w funkcji kwadratowej) z większą ilością warunków. Zauważ, że wyrażania warunkowe mogą być zagnieżdżone (tj. w instrukcjach pod jednym z warunków może znaleźć się kolejny if, a w nim kolejny, a w nim kolejny...):

#File: Roots.rb
puts "Define a in f(x) = ax**2+bx+c:"
a = gets.to_f
puts "Define b in f(x) = ax**2+bx+c:"
b = gets.to_f
puts "Define c in f(x) = ax**2+bx+c:"
c = gets.to_f
if a == 0
  if b == 0
    if c == 0
      puts "f(x) = 0 has an infinite number of roots ;)"
    else
      puts "f(x) = #{c} has no roots"
    end
  else
    puts "f(x) = #{b}x+#{c} has one root"
  end
else
  d = b**2 - 4*a*c
  if d > 0
    puts "f(x) = #{a}x**2+#{b}x+#{c} has two roots"
  elsif d == 0
    puts "f(x) = #{a}x**2+#{b}x+#{c} has one root"
  else
    puts "f(x) = #{a}x**2+#{b}x+#{c} has no roots"
  end
end 

Na koniec tego rozdziału mała ciekawostka. Skrócony zapis trójargumentowego wyrażenia warunkowego:

WARUNEK ? INSTRUKCJE_1 : INSTRUKCJE_2

Powyższy schemat kodu jest równoważny poniższemu:

if (WARUNEK)
  INSTRUKCJE_1
else
  INSTRUKCJE_1
end

Poniżej wykorzystanie operatora trójargumentowego na przykładzie naszego programu badającego podzielność przez 13 (operator trójargumentowy jest stosowany, gdy kod instrukcji do wykonania jest krótki - w przeciwnym wypadku, bardziej czytelny jest standardowy if):

#File: Divisibility3.rb
puts "Type a number:"
n = gets.to_f
puts "#{n} is#{ n%13==0 ? "" : " NOT" } divisible by 13"

 

Dla ciekawych świata:
Więcej informacji na temat wyrażeń warunkowych
 (między innymi wyrażenia unless oraz case, dla których zabrakło miejsca w samouczku ;))

 

Pętle

W poprzednim rozdziale poznaliśmy wyrażenia warunkowe. Czas dopełnić je pętlami i zacząć pisać naprawdę zaawansowane programy ;)

While

Podstawową pętlą w Rubym (i większości języków programowania) jest while. Składnia (słowo kluczowe do może być zamienione na backslash, średnik lub znak nowej linii):

while WARUNEK do
  INSTRUKCJE
end

Powyższy schemat kodu działa następująco: Jeśli spełniony jest WARUNEK, wykonywane są INSTRUKCJE, po czym jeśli spełniony jest WARUNEK, wykonywane są INSTRUKCJE, po czym… i tak w pętli ;). Poniżej pętla wypisująca 10 pierwszych liczb naturalnych:

#File: Loop.rb
i = 0
while i<10 do
  print "#{i}  "
  i = i+1 #Shorter syntax: i+=1
end
jan@Stacja-Linux ~/Pulpit $ ruby Loop.rb 
0  1  2  3  4  5  6  7  8  9  

Modyfikatory

Ruby posiada modyfikatory, które służą do zmiany składni (oraz działania) pewnych konstrukcji. Pętla while posiada taki modyfikator. Sprawia on, że zamiast sprawdzać warunek i wykonywać kod, pętla wykonuje kod, po czym sprawdza warunek ;). Modyfikator zapewnia, wykonanie się kodu wewnątrz pętli przynajmniej raz. Składnia:

begin
  INSTRUKCJE
end while WARUNEK

Przykład:

#File: Loop2.rb
i = 0
begin
  print "#{i}  "
  i += 1
end while  i<10
jan@Stacja-Linux ~/Pulpit $ ruby Loop2.rb 
0  1  2  3  4  5  6  7  8  9  

Wyrażenie warunkowe if również posiada modyfikator. Nie zmienia on jednak działania tej konstrukcji. Składnia:

INSTRUKCJE if WARUNEK

Przykład:

irb(main):001:0> puts "Hi" if 3>2 
Hi
=> nil
irb(main):002:0> puts "Hey" if 3>4 
=> nil
irb(main):003:0> 

For

Kolejną pętlą w Rubym (i nie tylko) jest for. Wykonuje ona pewne instrukcje dla każdej zmiennej z pewnego zbioru (wyrażenia). Składnia:

for NAZWA_ZMIENNEJ in WYRAŻENIE do
  INSTRUKCJE
end

Przykład:

#File: Loop3.rb
for i in 0...10 do
  print "#{i}  "
end
jan@Stacja-Linux ~/Pulpit $ ruby Loop3.rb 
0  1  2  3  4  5  6  7  8  9  

Pojawiła się pewna nowość - zakres. Zakresy to specjalne konstrukcje, które po kolei “podają” liczby z pewnego… zakresu ;) x...y oznacza liczby od x do y, natomiast x..y oznacza liczby od x do y włącznie.

Do pętli for wrócimy w następnym rozdziale. Teraz zajmiemy się dwoma ciekawymi słowami kluczowymi: break i next.

Break

Słowo kluczowe break służy do przerywania pętli. Poniżej kolejna wariacja programu sprawdzającego podzielność liczby przez 13:

 

#File: Loop4.rb
i = 0
while i<5 
  puts "Type a number (or \"exit\" if you want to quit) and press ENTER"
  input = gets
  break if input == "exit\n"
  n = input.to_f
  puts "#{n} is #{n%13==0 ? "" : " NOT" } divisible by 13"
  i+=1
end
puts "Thanks for using our software! If you want to check more than five numbers in one run, please download full version!"

Powyższy program sprawdza w pętli, czy dana liczba jest podzielna przez 13. Robi to jednak tylko 5 razy, po czym wyświetla stosowny komunikat ;) Co więcej, jeśli użytkownik wpisze “exit” program zakończy działanie dzięki słowu kluczowemu break. Zauważ, że metoda gets dodaje na końcu podanego tekstu znak nowej linii (stąd “\n” przy sprawdzaniu wartości zmiennej input). Może spróbujesz napisać pełną wersję programu? ;)

Next

Słowo kluczowe next umożliwia pominięcie jednego “obrotu pętli” (mówiąc inaczej: jednej iteracji). Poniżej wykorzystanie tego mechanizmu w uniknięciu błędu spowodowanego próbą dzielenia przez zero:

#File: Loop5.rb
for x in -5..5 
  next if x == 0
  puts "300/#{x} = #{300/x}"
end
jan@Stacja-Linux ~/Pulpit $ ruby Loop5.rb 
300/-5 = -60
300/-4 = -75
300/-3 = -100
300/-2 = -150
300/-1 = -300
300/1 = 300
300/2 = 150
300/3 = 100
300/4 = 75
300/5 = 60

Jeśli nie należysz do strachliwych usuń/zakomentuj drugą linię programu i zobacz co się wydarzy ;)

 

Dla ciekawych świata:
Więcej informacji o pętlach (między innymi pętle until oraz each, dla których zabrakło miejsca w samouczku ;)).
Zakresy w Rubym.

Zaawansowane typy danych

Teraz, gdy znasz już proste typy danych oraz konstrukcje dające pełną programistyczną moc, czas zapoznać się z bardziej złożonymi typami: tablicą i słownikiem.

Tablica (Array)

Dotychczas, w naszych programach mieliśmy, co najwyżej kilka zmiennych. W prawdziwym życiu programiści muszą borykać się z przypadkami, kiedy ilości danych "idą w terabajty". Wyobraź sobie sytuację, w której chcesz wpisać do programu średnią, dobową temperaturę wszystkich dni w minionym roku. Tworzenie osobnej zmiennej dla każdego dnia nie jest dobrym pomysłem (choćby z powodu czytelności kodu). Właśnie w takich sytuacjach przydają się tablice. Tablica to obiekt, który w uporządkowany sposób “przechowuje” inne obiekty. Przykład:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> a = [1, "word", 10, nil]
=> [1, "word", 10, nil]
irb(main):002:0>

Składnia jest bardzo intuicyjna. Tablicę tworzymy poprzez wpisanie oddzielonych przecinkami elementów w nawiasy kwadratowe. Aby otrzymać element (i go zmodyfikować) o indeksie n, z tablicy a, należy wpisać a[n] (pamiętaj, że programiści numerują od zera, a nie od jedynki ;)):

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> a = [1, "word", 10, nil]
=> [1, "word", 10, nil]
irb(main):002:0> a[2]
=> 10
irb(main):003:0>a[0]+=100
=> 101

Aby otrzymać zakres elementów możemy użyć… zakresu ;) W ten sam sposób możemy uzyskać część napisu:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> a = [1, "word", 10, nil]
=> [1, "word", 10, nil]
irb(main):002:0> a[2]
=> 10
irb(main):003:0>a[0]+=100
=> 101
irb(main):004:0> a[1..3]
=> ["word", 10, nil]
irb(main):005:0>"Dog, cat and hamster"[0..7]
=> "Dog, cat"

Aby dodać element na końcu tablicy powinniśmy użyć metody push, lub operatora <<, który de facto jest skróconą formą zapisu tej metody. Jeśli chcesz umieścić element w konkretnym miejscu tablicy - użyj metody insert:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> a = [1, "word", 10, nil]
=> [1, "word", 10, nil]
irb(main):002:0> a[2]
=> 10
irb(main):003:0>a[0]+=100
=> 101
irb(main):004:0> a[1..3]
=> ["word", 10, nil]
irb(main):005:0>"Dog, cat and hamster"[0..7]
=> "Dog, cat"
irb(main):006:0> a.push(96)
=> [101, "word", 10, nil, 96]
irb(main):007:0> a << "a"
=> [101, "word", 10, nil, 96, "a"]
irb(main):008:0> a.insert(2, nil)
=> [101, "word", nil, 10, nil, 96, "a"]

Analogicznie (przy usuwaniu) do metod push i insert działają pop oraz delete_at:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> arr = [1,2,3,4,5,6,7,8,9]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):002:0> arr.pop
=> 9
irb(main):003:0> arr.delete_at(3)
=> 4

Zauważ, że metody push, << oraz insert zwracają całą liste, natomiast pop oraz delete_at tylko usunięty element.

Słownik (Hash)

Słowniki, nazywane są też tablicami asocjacyjnymi. Działają bardzo podobnie do zwykłych tablic, ale wartości mogą być indeksowane dowolnym typem obiektu (w przypadku tablic są to liczby całkowite). Innymi słowy, słowniki to zbiory par klucz=>wartość. Zauważ, że o ile wartości mogą się powtarzać, klucz musi być jednoznaczny. Poniżej kilka równoważnych sposobów inicjalizacji słownika:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> h1 = {"a"=>1, "b"=>2, "c"=>4, "d"=>4}
=> {"a"=>1, "b"=>2, "c"=>4, "d"=>4}
irb(main):002:0> h2 = Hash["a"=>1, "b"=>2, "c"=>4, "d"=>4]
=> {"a"=>1, "b"=>2, "c"=>4, "d"=>4}
irb(main):003:0> h3 = Hash["a", 1, "b", 2, "c", 4, "d", 4]
=> {"a"=>1, "b"=>2, "c"=>4, "d"=>4}

Aby uzyskać element zapisany pod danym kluczem (i go zmodyfikować), należy użyć składni analogicznej do tej z tablic:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0>  h = {"a"=>1, "b"=>nil, "c"=>123}
=> {"a"=>1, "b"=>nil, "c"=>123}
irb(main):002:0> h["a"]
=> 1
irb(main):003:0> h["b"]=1000
=> 1000
irb(main):004:0> h["d"]
=> nil
irb(main):005:0> h
=> {"a"=>1, "b"=>1000, "c"=>123}

Zauważ, że, gdy “poprosimy” słownik o wartość pod kluczem, którego w nim nie ma, zwrócony będzie nil (analogiczne zachowanie występuje w tablicach).

Aby dodać parę klucz=>wartość do słownika należy użyć poniższej składni:

 

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0>  h = {"a"=>1, "b"=>2, "c"=>3
=> {"a"=>1, "b"=>2, "c"=>3}
irb(main):002:0>h["z"] = 1.11
=> 1.11
irb(main):003:0> h
=> {"a"=>1, "b"=>2, "c"=>3, "z"=>1.11}

Jeśli nie należysz do strachliwych spróbuj zastosować/zaadoptować tą składnię dla tablic ;)

Do usuwania elementów ze słownika służy metoda delete:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0>  h = {"a"=>1, "b"=>2, "c"=>3
=> {"a"=>1, "b"=>2, "c"=>3}
irb(main):002:0>h["z"] = 1.11
=> 1.11
irb(main):003:0> h
=> {"a"=>1, "b"=>2, "c"=>3, "z"=>1.11}
irb(main):004:0> h.delete("a")
=> 1
irb(main):005:0> h
=> {"a"=>1, "c"=>123, "z"=>1.11}

Symbol

Symbol to typ służący do reprezentowania nazw. Najczęściej wykorzystuje się je jako klucze w słownikach, gdyż są bardziej wydajne (dzięki nim programy działają szybciej) niż napisy.

Tworzymy je za pomocą dwukropka i następujących po nim liter i cyfr:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> s1 = :dog 
=> :dog
irb(main):002:0> h1 = {:one => 1, :two => 2}
=> {:one=>1, :two=>2}
irb(main):003:0> h2 = {one: 1, two: 2}
=> {:one=>1, :two=>2}

Zauważ, że jeśli używamy symboli jako kluczy słownika możemy użyć specjalnej skróconej formy inicjalizacji słownika, gdzie dwukropek należący do symbolu przestawiamy na jego koniec. Pozwala to pomijać znak => (tzw. hash rocket).

Iterowanie po tablicach i słownikach

Tablice i słowniki pozwalają na wykonywanie pętli po ich kolejnych elementach (tak jak zakresy). Poniżej przykładowy program z użyciem pętli for:

Iterating.rb
a = [1,3,7,"end"]
h = {a: 1, b:"B", c:8}
for i in a
  puts i
end
for i,j in h
  puts i
  puts j
end
jan@Stacja-Linux ~/Pulpit $ ruby Iterating.rb 
1
3
7
end
a
1
b
B
c
8

Zauważ, że iterując po słowniku potrzebne są dwie zmienne (w naszym wypadku i oraz j) na które przypisywane będą kolejne elementy, gdyż element słownika to para klucz=>wartość.

Tablice tablic

Elementem tablicy może być dowolny obiekt, w tym... tablica. Zagnieżdżone tablice do dosyć naturalna reprezentacja macierzy (dwu- i więcej wymiarowych). Poniżej przykład inicjalizacji takiej macierzy oraz wypisanie jej elementu (zauważ jakie to proste i eleganckie ;)):

jan@Stacja-Linux ~ $ irb
irb(main):001:0> matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
irb(main):002:0> matrix[2][0]
=> 9

Przegląd najważniejszych metod (i operatorów) dla list i słowników.

Metody wymienione wcześniej w rozdziale nie są wypisane poniżej.

Tablice

Metoda Opis
count(n) Zwraca ilość elementów w tablicy (jeśli nie podamy argumentu n) lub ilość elementów równych n w tablicy.
+ Zwraca konkatenację podanych tablic.
& Zwraca część wspólną podanych tablic.
clear Usuwa wszystkie elementy z listy.
index(obj) Zwraca indeks pierwszego elementu równego obj w tablicy lub nil jeśli takowego w niej nie ma.
reverse Zwraca tablicę z odwróconą kolejnością elementów.
sort Zwraca tablicę z posortowanymi elementami.
uniq Zwraca tablicę z usuniętymi powtórzeniami elementów.

Poniżej praktyczne spojrzenie na powyższe metody:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0>  a = [1,2,3,4,5,6,7,8,9]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):002:0>  a.count 8
=> 1
irb(main):003:0>  a.count 11
=> 0
irb(main):004:0>  a+a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):005:0> a&[0,5,10]
=> [5]
irb(main):006:0> a.index 2
=> 1
irb(main):007:0> a.index 11
=> nil
irb(main):008:0> a.reverse
=> [9, 8, 7, 6, 5, 4, 3, 2, 1]
irb(main):009:0> a.sort
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):010:0> [9,3,2,9,2,3,4,5,1,2,3,1].sort
=> [1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 9, 9]
irb(main):011:0> ["aba","aca","a","b"].sort
=> ["a", "aba", "aca", "b"]
irb(main):012:0> [1,1,1,1,1,2,2,2,2,1,1,1,2,2,2,3,4,5,6,5,4,3,4,5,6].uniq
=> [1, 2, 3, 4, 5, 6]
irb(main):013:0> a.clear
=> []
irb(main):014:0> a
=> []

Słowniki

Metoda Opis
length Zwraca liczbę elementów w słowniku.
has_key? Zwraca true, jeśli słownik zawiera podany klucz  i false w przeciwnym przypadku.
has_value? Zraca true, jeśli słownik zawiera podaną wartość i false w przeciwnym przypadku.
clear Usuwa wszystkie elementy ze słownika.
invert Zwraca słownik, w którym wartości podanego słownika są kluczami i odwrotnie.
merge(other_hash) Zwraca słownik będący połączeniem dwu podanych słowników.

Poniżej powyższe metody w przykładach:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> h = {a: 1, b: 2, c: 3}
=> {:a=>1, :b=>2, :c=>3}
irb(main):002:0> h.length
=> 3
irb(main):003:0> h.has_key? :a
=> true
irb(main):004:0> h.has_key? :q
=> false
irb(main):005:0> h.has_value? :a
=> false
irb(main):006:0> h.has_value? 1
=> true
irb(main):007:0> h.merge({d: 4, e: 5})
=> {:a=>1, :b=>2, :c=>3, :d=>4, :e=>5}
irb(main):008:0> h.invert
=> {1=>:a, 2=>:b, 3=>:c}
irb(main):009:0> h.clear
=> {}
irb(main):010:0>

Po co komu podłoga?

Kilka rozdziałów temu mówiliśmy sobie o dziwnym zachowaniu Ruby’iego przy dzieleniu liczb całkowitych:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0> 9/5
=> 2

Poniżej krótki przykład pokazujący, że czasem jest to bardzo użyteczny feature:

jan@Stacja-Linux ~/Pulpit $ irb
irb(main):001:0>  a = [1,2,3,4,5,6,7,8,9]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):002:0>  a[a.length/2]
=> 5

 

Dla ciekawych świata:
Kompletny przegląd metod (i garść informacji) dla tablic, słowników i symboli.

Operacje na plikach

Umiesz już posługiwać się wszystkimi podstawowymi mechanizmami Ruby’iego, jak i programowania w ogóle. Na koniec tego samouczka przygotowaliśmy coś co przyda Ci się przy rozwiązywaniu zadań z naszego serwisu, jak i w codziennej pracy przy komputerze. Poniższy rozdział traktuje o operacjach na plikach tekstowych.

Czytanie i tworzenie do plików

Ruby posiada metodę File.open(NAZWA_PLIKU, TRYB), która w zależności od wybranego trybu otwiera/tworzy plik pozwalając na odczyt/zapis danych:
 

Tryb Opis
r Tylko do odczytu.
r+ Odczyt-zapis.
w Tylko zapis. Jeśli plik o danej nazwie istnieje, zostanie podmieniony. Jeśli nie istnieje, zostanie stworzony.
w+ Odczyt-zapis. Jeśli plik o danej nazwie istnieje, zostanie podmieniony. Jeśli nie istnieje, zostanie stworzony.
a Tylko zapis. Jeśli plik o danej nazwie istnieje, dane zostaną dopisane na jego końcu. Jeśli nie istnieje, zostanie stworzony.
a+ Odczyt-zapis. Jeśli plik o danej nazwie istnieje, dane zostaną dopisane na jego końcu. Jeśli nie istnieje, zostanie stworzony.

Uwaga! Po otwarciu pliku metodą open, zawsze należy go zamknąć. W przeciwnym wypadku plik może ulec uszkodzeniu. Służy do tego metoda close. Poniżej przykładowy program, który tworzy plik, zapisuje w nim dwa zdania za pomocą metody puts i zamyka go:

#File: Write_file.rb
file = File.open "New_file.txt", "w"
file.puts("I need to read something about OOP")
file.puts("I need to read something about RoR")
file.close

Oprócz metody puts, która dodaje na końcu napisu znak nowej linii istnieje również metoda print, która tego nie robi. Niewątpliwie widać tu analogię to metod wypisujących napis na ekran. Zauważ, jednak, że składnia jest inna.

Wczytajmy teraz nasz plik i wyświetlmy kolejno wszystkie jego linie za pomocą pętli for (w Rubym możemy iterować po liniach pliku ;)):

#File: Read_file.rb
file = File.open "New_file.txt", "r"
for line in file
  puts line
end
file.close
jan@Stacja-Linux ~/Pulpit $ ruby Read_file.rb 
I need to read something about OOP
I need to read something about RoR

Epilog

Jeśli nie ominąłeś/nie ominęłaś żadnej lekcji i trafiłeś/trafiłaś aż tutaj, oznacza to, że posiadasz podstawy wiedzy o programowaniu w Rubym i możesz powoli oswajać się z tutułem Rubyist ;)

Nie spoczywaj jednak na laurach... Do zostania mistrzem rubinów jeszcze daleka droga... 

Samouczek, który właśnie dobiegł końca miał na celu wprowadzenie czytelnika w świat Ruby'iego (i programowania jako takiego). Ze względu na jego skończony rozmiar, niektóre informacje nie znalazły w nim swojego miejsca, ale internet to nieskończone źródło wiedzy, więc dalej możesz rozwijać swoje umiejętności programistyczne. Na początek, proponujemy abyś zapoznał/zapoznała się z RubyGems, czyli systemem pakietowania, dzięki któremu zyskasz jeszcze więcej programistycznej mocy oraz OOP (Object Oriented Programming - Programowanie Obiektowe) w Rubym, a następnie poczytał/poczytała o frameworku Ruby on Rails (RoR). Pamiętaj też, że programistyczny świat nie kończy się na Rubym ;)

Powodzenia w dalszym zdobywaniu wiedzy!