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.