Ruby - wprowadzenie, część 2 - właściwe początki
środa, 8 luty 2006, w kategoriach: Programowanie, Ruby
Okej, pierwszą zaprawę mamy już za sobą. Dziś przyjrzymy się podstawowym “klockom”, z których później (w następnym odcinku) spróbujemy zbudować już coś wykonującego jakieś konkretne zadanie. Z góry zastrzegam, że staram się, żeby tekst był zrozumiały dla jak największej grupy osób, dlatego tłumacze tak łoptologicznie jak umiem, w związku z czym używam wielu uproszczeń i skrótów myślowych, które z formalnego punktu widzenia zapewne bywają niezbyt poprawne. Ale wytykać można, chętnie posłucham.
A więc, deklaracja zmiennych (identyfikatorów w formie ciągu znaków, przypisanych danej wartości) i tablic (o których można myśleć, jak o zmiennych posiadających więcej niż jedną wartość) oraz tablic asocjacyjnych (skojarzeniowych, słowników, hash-table) przebiega podobnie jak w innych językach:
irb(main):001:0> napis = "trescnapisu" => "trescnapisu" irb(main):002:0> liczba = 1234 => 1234 irb(main):003:0> tablica = [ 1, 2, 3, 4 ] => [1, 2, 3, 4] irb(main):004:0> tablica[2] => 3 irb(main):005:0> hasz = { "jeden" => 1, "dwa" => 2, "trzy" => 3 } => {"jeden"=>1, "dwa"=>2, "trzy"=>3} irb(main):006:0> hasz["jeden"] => 1
Jak widać do elementów tablicy odwołujemy się po numerze danego elementu, przy czym pierwszy element ma indeks 0. Do tablicy skojarzeniowej zaś odwołujemy się po kluczu przypisanym danej wartości.
Gdy mamy już tablicę czy hasz, fajnie by było móc zrobić coś dla każdego elementu tejże. Można to zrobić na modłe “klasycznych” języków jak Java czy C++, w ten sposób:
irb(main):001:0> tablica = [1, 2, 3, 4] irb(main):002:0> for i in 0..tablica.length - 1 irb(main):003:1> puts tablica[i] irb(main):004:1> end 1 2 3 4 => 0..3
“..” to operator zakresu. Pisząc więc:
irb(main):005:0> for i in 0..tablica.length-1
mamy na myśli “Dla i z zakresu od 0 do długości tablicy pomniejszonej o 1 zrób to i to ze zmienną ‘i’” (indeks ostatniego elementu w tablicy jest równy długości tej tablicy minus jeden, z racji tego, że indeks pierwszego elementu jest równy zero).
A tak przy okazji, gdyby ktoś jeszcze nie zauważył, nawiasy otaczające argumenty podawane procedurze są opcjonalne więc: ‘puts “foo”‘ działa równie dobrze jak: ‘puts(”foo”)’ - widać więc, że Ruby jest w kwestii składni bardzo swobodny, a dużo rzeczy jest raczej kwestią konwencji czy własnych preferencji, a nie ograniczeń narzucanych przez język.
Wracając do iterowania po tablicy, użycie pętli for to niezbyt Ruby’owe podejście. Ktoś bardziej biegły w tym języku napisałby raczej:
irb(main):011:0> tablica.each { |element| puts element } 1 2 3 4 => [1, 2, 3, 4]
Dla hasza możemy wyświetlić same wartości, same klucze, bądź jedno i drugie:
irb(main):014:0> hasz.each { |element| puts element } jeden 1 dwa 2 trzy 3 => {"jeden"=>1, "dwa"=>2, "trzy"=>3} irb(main):015:0> hasz.each_value { |element| puts element } 1 2 3 => {"jeden"=>1, "dwa"=>2, "trzy"=>3} irb(main):016:0> hasz.each_key { |element| puts element } jeden dwa trzy => {"jeden"=>1, "dwa"=>2, "trzy"=>3}
Blok zawarty pomiędzy nawiasami klamrowymi lub słowami kluczowymi “do” i “end” oznacza “domknięcie” (ang. “closure”, konwencja nazewnicza każe używać nawiasów klamrowych dla domknięć jednolinijkowych, a do i end dla tych dłuższych), zwaną też po prostu “blokiem”. Domknięcie to blok kodu który będzie wykonany wewnątrz danej procedury - autor procedury pobierającej domknięcie jako argument można zrobić coś zarówno przed jej wywołaniem, jak i po, może też wywołać blok wiele razy w różnych miejscach. Blok ma dostęp do pewnych zmiennych zdeklarowanych gdzieś w śrdoku tej procedury, - to, jakie są to zmienne trzeba sprawdzić w dokumentacji, a następnie podać pomiędzy tymi pionowymi kreskami - “|” nasze nazwy dla tych zmiennych. Takie bloki są jednym z podstawowych elementów Ruby’ego i używane są często i gęsto. Do momentu w którym nie doświadczymy tego efektu “Aha!” wydają się one odrobine dziwne, głównie dlatego że rzadko spotyka się je w innych popularnych językach (choć to się zaczyna powoli zmieniać). Później za to można samemu wpaść z wielką przyjemnością na 101 rzeczy które domknięcia ułatwiają.
No właśnie, procedury. Definiujemy je w ten sposób:
irb(main):001:0> def procedura irb(main):002:1> puts "3l0 świat" irb(main):003:1> end => nil irb(main):004:0> procedura 3l0 świat => nil
Blok kodu, wołamy za pomocą “yield”, jako argument podając zmienne, które mają być blokowi przekazane, możemy więc zrobić tak:
irb(main):009:0> def procedura2 irb(main):010:1> zmienna1 = 100 irb(main):011:1> zmienna2 = 200 irb(main):012:1> yield zmienna1, zmienna2 irb(main):013:1> end => nil irb(main):014:0> procedura2 do |argument1, argument2| irb(main):015:1* puts argument1 irb(main):016:1> puts argument2 irb(main):017:1> end 100 200 => nil irb(main):018:0> procedura2 do |argument1, argument2| irb(main):019:1* puts argument1 + argument2 irb(main):020:1> end 300 => nil
Wybiegamy tu może trochę za podstawy, ale zależy mi żeby każdy kto tylko jest w stanie jak najwcześniej zrozumiał jak działają bloki, gdyż są one wykorzystywane często i gęsto w bibliotece standardowej Ruby’ego, pozatym są jedną z ciekawszych mechanizmów w Rubym.
Zanim zaczniemy pisać cokolwiek praktycznego, dobrze byłoby znać poza mechanizmami językowymi, także pare kluczowych kawałków biblioteki standardowej. Tak więc np. pobranie jakiejś wartości ze standardowego wejścia (czyli w wierszu poleceń, od użytkownika) wygląda tak:
irb(main):001:0> while true irb(main):002:1> linia = gets irb(main):003:1> if linia.chop == "koniec" irb(main):004:2> break irb(main):005:2> end irb(main):006:1> puts linia irb(main):007:1> end
Widzimy tu sporo nowych rzeczy. Po pierwsze mamy pętle while, która jak wiadomo wykonuje kod zawarty pomiędzy “while cośtam”, a pasującym “endem” (pasuję na listingu ten który zaczyna się w tej samej kolumnie, ale Ruby nie uzależnia sposobu wykonania programu od wcięć, tak jak Python) dopóki warunek logiczny następujący po słowie “while” jest spełniony.
Następnie zmiennej “linia” przypisujemy wartość pobraną od użytkownika, potem sprawdzamy czy linia ta (po usunięciu znaku nowej linii z jej końca za pomocą “.chop”) nie brzmi czasem “koniec” i jeśli tak jest, przerywamy pętle. W przeciwnym razie po prostu wyświetlamy ponownie linię podaną nam przez użytkownika. I zaczynamy całą zabawę od nowa.
Wyświetlenie zawartości pliku jest nawet łatwiejsze:
irb(main):002:0> puts IO.readlines("/sciezka/do/pliku")
tudzież pod Windows:
irb(main):002:0> puts IO.readlines("c:\sciezka\do\pliku")
A teraz wyskoczmy przynajmniej na moment z interpretera, aby przećwiczyć pobieranie argumentów z linii poleceń. Właściwie jedyne co trzeba tu powiedzieć to to, że są one przechowywane w tablicy o nazwie ARGV. Tak więc możemy napisać już cud-program wyswietlplik.rb, tym razem w edytorze:
puts IO.readlines(ARGV[0])
I uruchomić go za pomocą polecenia ruby wyswietlplik.rb /sciezka/do/pliku.
W tym momencie elementarz programowania proceduralnego mamy za sobą, to co jest nam teraz potrzebne to mechanizmy programowania obiektowego i dużo, dużo biblioteki standardowej. Ale to już temat na odcinek kolejny, oby wena dopisywała mi dalej :>
Dodaj do del.icio.us | Dodaj do wykop.pl
Komentarze ():
lopex, 11 luty 2006, 5:02 pm
for .. in to jest syntactic sugar dla each,
ja wolę each
sztywny, 11 luty 2006, 6:02 pm
Napisałem przecież:
“Wracając do iterowania po tablicy, użycie pętli for to niezbyt Ruby’owe podejście. Ktoś bardziej biegły w tym języku napisałby raczej: …”
Też wolę each :)
lopex, 11 luty 2006, 6:02 pm
Mi chodziło tylko ze for .. in używa each’a, trochę się zdziwiłem jak się o tym dowiedziałem
sztywny, 11 luty 2006, 9:02 pm
Ano, ciekawe :)
michał, 12 luty 2006, 10:02 pm
Fajnie napisana czekam kiedy bedzie tutorial o ruby on rails
RubySchoolboy, 20 kwiecień 2006, 10:04 am
tudzież pod windows zamiast
IO.readlines(”c:sciezkadopliku”)
powinno być
IO.readlines(”c:\sciezka\do\pliku”)
bynajmniej w FreeRIDE :)
bolek, 16 maj 2006, 8:05 pm
Wszystko jedno, moze byc:
IO.readlines(”c:sciezkadopliku”)
IO.readlines(”c:\sciezkado\pliku”)
jak i
IO.readlines(”c:/sciezka/do/pliku”)
Przynajmniej u mnie
sztywny, 17 maj 2006, 6:05 am
Faktycznie, powinno być escapnięte, jakoś mi umknął ten komentarz - sypie się pewnie zależnie od tego jaka jest faktycznie “sciezkadopliku”. Poprawiono.
Natalia, 1 luty 2009, 10:44 pm
:) Dzięki za tutorial. Świetny!