|
- Programming4Fun http://www.programming4fun.pun.pl/index.php - Poradniki http://www.programming4fun.pun.pl/viewforum.php?id=15 - Podstawy języka Ruby http://www.programming4fun.pun.pl/viewtopic.php?id=5 |
| arnon - 2014-07-09 21:44:40 |
Wprowadzenie Kod:irb(main):001:0> Ok, udało się otworzyć IRB’a. Co teraz? Kod:irb(main):001:0> "Hello World" => "Hello World" Ruby Wykonał Polecenie! Kod:irb(main):002:0> puts "Hello World" Hello World => nil puts jest poleceniem, które ogólnie rzecz biorąc wyświetla różne rzeczy w Rubim. A co oznacza => nil ? To wynik wyrażenia. puts zawsze zwraca nil, nil w Rubim zawsze oznacza kompletny brak jakiejkolwiek wartości. Kod:irb(main):003:0> 3+2 => 5 Trzy plus dwa. Wystarczająco proste. Co z “trzy razy dwa” (three times two)? Możesz to napisać, jest to wciąż bardzo krótkie, ale możesz też cofnąć się i zmienić to co przed chwilą wpisałeś. Wciśnij strzałkę-w-górę na swojej klawiaturze i zobacz czy pojawi się linią z tekstem 3+2 . Jeśli pojawi się, wtedy możesz użyć strzałki w lewo aby ustawić kursor zaraz za znakiem plus + nacisnąć klawisz backspace i zamienić plus na *. Kod:irb(main):004:0> 3*2 => 6 Następnie, wypróbujmy potęgowania: Kod:irb(main):005:0> 3**2 => 9 W Rubim ** jest sposobem zapisu potęgowania. Co jeśli chciałbyś pójść odwrotną drogą i obliczyć pierwiastek kwadratowy danej liczby ? Kod:irb(main):006:0> Math.sqrt(9) => 3.0 Co przedsatwia powyższa linia ? Tak, zgadza się, obliczamy tutaj pierwiastek kwadratowy liczby 9. Przypatrzmy się temu bliżej. Po pierwsze co oznacza Math ? Kod:irb(main):007:0> a = 3 ** 2 => 9 irb(main):008:0> b = 4 ** 2 => 16 irb(main):009:0> Math.sqrt(a+b) => 5.0 Odbiegamy tutaj jednak od tradycyjnego programu “Hello World” , na którym tutoriale dla początkujących powinny się skupiać więc wróćmy do niego. Kod:irb(main):010:0> def h irb(main):011:1> puts "Hello World!" irb(main):012:1> end => nil Kod def h rozpoczyna definicję metody. Wskazuje Rubiemu, że definiujemy metodę, która nazywa się h. Następna linia to ciało metody, ten sam kod, który widzieliśmy już wcześniej: puts "Hello World". W końcu ostatnia linia end mówi Rubiemu, że skończyliśmy definiować metodę. Odpowiedź Rubiego => nil mówi nam, że zrozumiał to, że skończyliśmy definiować metodę. Kod:irb(main):013:0> h Hello World! => nil irb(main):014:0> h() Hello World! => nil No cóż, to było proste. Wywołanie metody w Rubim sprowadza się do napisania jej nazwy. Jeśli metoda nie przyjmuje żadnych parametrów, to wszystko czego potrzebujesz. Możesz dodać puste nawiasy, jeśli chcesz ale nie jest to wymagane. Kod:irb(main):015:0> def h(name)
irb(main):016:1> puts "Hello #{name}!"
irb(main):017:1> end
=> nil
irb(main):018:0> h("Matz")
Hello Matz!
=> nilDziała … ale spójrzmy bliżej na to co się tutaj dzieje. Kod:irb(main):019:0> def h(name = "World")
irb(main):020:1> puts "Hello #{name.capitalize}!"
irb(main):021:1> end
=> nil
irb(main):022:0> h "chris"
Hello Chris!
=> nil
irb(main):023:0> h
Hello World!
=> nilWarto zwrócić tutaj uwagę na kilka innych sztuczek. Jedną z nich jest to, że wywołujemy metodę bez nawiasów. Jeśli to co robisz jest oczywiste, nawiasy są opcjonalne. Inną sztuczką jest domyślny parametr World. Oznacza on “Jeśli imię nie jest podane, użyj domyślnej wartości "World"”. Kod:irb(main):024:0> class Greeter
irb(main):025:1> def initialize(name = "World")
irb(main):026:2> @name = name
irb(main):027:2> end
irb(main):028:1> def say_hi
irb(main):029:2> puts "Hi #{@name}!"
irb(main):030:2> end
irb(main):031:1> def say_bye
irb(main):032:2> puts "Bye #{@name}, come back soon."
irb(main):033:2> end
irb(main):034:1> end
=> nilNowym słowem kluczowym jest tutaj class. Definiuje ono nową klasę nazwaną Greeter oraz kilka metod tej klasy. Zauważ również @name. Jest to zmienna instancji, która jest dostępna dla wszystkich metod tej klasy. Używana jest przez metody say_hi oraz say_bye. Kod:irb(main):035:0> g = Greeter.new("Pat")
=> #<Greeter:0x16cac @name="Pat">
irb(main):036:0> g.say_hi
Hi Pat!
=> nil
irb(main):037:0> g.say_bye
Bye Pat, come back soon.
=> nilGdy obiekt g jest utworzony, zapamiętuje on imię jako Pat. Hmm, co jeśli chcielibyśmy dostać się bezpośrednio do imienia ? Kod:irb(main):038:0> g.@name
SyntaxError: compile error
(irb):52: syntax error
from (irb):52Niestety, nie możemy tak zrobić. Kod:irb(main):039:0> Greeter.instance_methods
=> ["method", "send", "object_id", "singleton_methods",
"__send__", "equal?", "taint", "frozen?",
"instance_variable_get", "kind_of?", "to_a",
"instance_eval", "type", "protected_methods", "extend",
"eql?", "display", "instance_variable_set", "hash",
"is_a?", "to_s", "class", "tainted?", "private_methods",
"untaint", "say_hi", "id", "inspect", "==", "===",
"clone", "public_methods", "respond_to?", "freeze",
"say_bye", "__id__", "=~", "methods", "nil?", "dup",
"instance_variables", "instance_of?"]Dużo metod. My zdefiniowaliśmy tylko dwie metody. Co tutaj się dzieje? No coż, to są wszystkie metody obiektów klas Greeter, kompletna lista, wliczając w to metody zdefiniowane w klasach nadrzędnych. Jeśli chcemy wyświetlić tylko metody zdefiniowane w klasie Greeter, możemy wskazać, że nie chcemy wyświetlać metod zdefiniowanych w klasach nadrzędnych poprzez przekazanie parametru false. Kod:irb(main):040:0> Greeter.instance_methods(false) => ["say_bye", "say_hi"] Zobaczmy więc, na jakie metody nasz obiekt odpowiada (responds_to): Kod:irb(main):041:0> g.respond_to?("name")
=> false
irb(main):042:0> g.respond_to?("say_hi")
=> true
irb(main):043:0> g.respond_to?("to_s")
=> trueRozumie więc say_hi, oraz to_s (oznaczającą zamienienie czegoś w stringa, metoda która jest zdefiniowana domyślnie dla każdego obiektu), ale nie wie nic o metodzie name. Kod:irb(main):044:0> class Greeter irb(main):045:1> attr_accessor :name irb(main):046:1> end => nil W Rubim możesz ponownie otworzyć klasę i zmodyfikować ją. To nie zmienia żadnych obiektów, które juz isnieją, ale zmienia obiekty, które dopiero powstaną. Utwórzmy więc nowy obiekt i pobawmy się jego właściwością @name. Kod:irb(main):047:0> g = Greeter.new("Andy")
=> #<Greeter:0x3c9b0 @name="Andy">
irb(main):048:0> g.respond_to?("name")
=> true
irb(main):049:0> g.respond_to?("name=")
=> true
irb(main):050:0> g.say_hi
Hi Andy!
=> nil
irb(main):051:0> g.name="Betty"
=> "Betty"
irb(main):052:0> g
=> #<Greeter:0x3c9b0 @name="Betty">
irb(main):053:0> g.name
=> "Betty"
irb(main):054:0> g.say_hi
Hi Betty!
=> nilUżycie attr_accessor zdefiniowało dwie nowe metody dla nas, name aby odczytać wartość oraz name= aby ją ustawić. Kod:#!/usr/bin/env ruby
class MegaGreeter
attr_accessor :names
# Create the object
def initialize(names = "World")
@names = names
end
# Say hi to everybody
def say_hi
if @names.nil?
puts "..."
elsif @names.respond_to?("each")
# @names is a list of some kind, iterate!
@names.each do |name|
puts "Hello #{name}!"
end
else
puts "Hello #{@names}!"
end
end
# Say bye to everybody
def say_bye
if @names.nil?
puts "..."
elsif @names.respond_to?("join")
# Join the list elements with commas
puts "Goodbye #{@names.join(", ")}. Come back soon!"
else
puts "Goodbye #{@names}. Come back soon!"
end
end
end
if __FILE__ == $0
mg = MegaGreeter.new
mg.say_hi
mg.say_bye
# Zmień imię na "Zeke"
mg.names = "Zeke"
mg.say_hi
mg.say_bye
# Zmień imię na tablicę imion
mg.names = ["Albert", "Brenda", "Charles",
"Dave", "Engelbert"]
mg.say_hi
mg.say_bye
# Zmień imię na nil
mg.names = nil
mg.say_hi
mg.say_bye
endZapisz ten plik pod nazwą “ri20min.rb”, i uruchom go wpisując “ruby ri20min.rb”. Wynik poniżej: Kod:Hello World! Goodbye World. Come back soon! Hello Zeke! Goodbye Zeke. Come back soon! Hello Albert! Hello Brenda! Hello Charles! Hello Dave! Hello Engelbert! Goodbye Albert, Brenda, Charles, Dave, Engelbert. Come back soon! ... ... W powyższym przykładzie jest sporo nowych rzeczy, którym możemy się bliżej przyjrzeć. Kod:# Powiedz hi wszystkim
def say_hi
if @names.nil?
puts "..."
elsif @names.respond_to?("each")
# @names to pewnego rodzaju lista, przejdź po elementach tej listy
@names.each do |name|
puts "Hello #{name}!"
end
else
puts "Hello #{@names}!"
end
endMetoda ta sprawdza teraz parametr @names i jeśli wynosi on nil to wyświetla trzy kropki. Nie ma sensu witać kogoś kogo nie ma, prawda ? Kod:@names.each do |name|
puts "Hello #{name}!"
endeach jest metodą, która przyjmuje blok kodu i wywołuje ten blok kodu dla każdego elementu na liście. Kod pomiędzy do oraz end jest własnie takim blokiem. Blok przypomina anonimową funkcję lub lambda. Zmienna pomiędzy pionowymi liniami jest parametrem przekazywanym do bloku. Kod:for (i=0; i<number_of_elements; i++)
{
do_something_with(element[i]);
}To działa, ale nie jest zbyt eleganckie. Potrzebujesz tu tymczasowej zmiennej do przechowania rozmiaru listy i oraz musisz opisać jak dostać się do kolejnych elementów listy. Sposób w jaki robi to Ruby jest o wiele bardziej elegancki, wszystko ukryte jest za metodą each, musisz jedynie wskazać co chcesz zrobić z każdym elementem. Wewnątrz, metoda each tak naprawdę wywołuje yield "Albert", później yield "Brenda" i w końcu yield "Charles", itd. Kod:# Powiedz wszystkim "do widzenia"
def say_bye
if @names.nil?
puts "..."
elsif @names.respond_to?("join")
# Połącz elementy listy przecinkiem
puts "Goodbye #{@names.join(", ")}. Come back soon!"
else
puts "Goodbye #{@names}. Come back soon!"
end
endMetoda say_bye nie korzysta z each, zamiast tego sprawdza czy @names odpowiada na wywołanie metody join, jeśli tak, to uzywa tej metody. W przeciwnym wypadku, funkcja wyświetla po prostu zmienną jako stringa. Ten sposób, który polega na nie przejmowaniu się faktycznym typem zmiennej a poleganiu po prostu na metodach jakie są dostępne nazywa się “Duck Typing” (dosł kacze typowanie), tak jak w powiedzeniu “jeśli to chodzi jak kaczka, i kwacze jak kaczka to musi być kaczka…”. Zaletą tego podejścia jest to, że nie musimy się niepotrzebnie ograniczać typami jakie możemy wspierać. Jeśli ktoś wymyśli nowy typ listy, to jeśli tylko ten nowy typ będzie wspierał metodę join używając tej samej sematyki jak w przypadku innych list, wszystko będzie działać tak jak powinno. Kod:if __FILE__ == $0 __FILE__ jest magiczną zmienną która zawiera nazwę obecnie używanego pliku. $0 jest nazwą pliku użytego do uruchomienia programu. To przyrównanie oznacza: “Jeśli obecny plik to plik, który został uruchomiony wtedy” takie rozwiązanie umożliwia użycie tego pliku jako biblioteki w innych miejscach i nie wykonywanie tego kodu w tym kontekście. Jeśli jednak plik jest użyty jako główny plik wykonywalny wtedy kod zostanie wykonany. |