Programowanie w językach skryptowych wykład 5 Ruby

Podobne dokumenty
Programowanie obiektowe

Języki i techniki programowania Ćwiczenia 2

Przeciążanie operatorów

Programowanie strukturalne. Opis ogólny programu w Turbo Pascalu

Informatyka I. Typy danych. Operacje arytmetyczne. Konwersje typów. Zmienne. Wczytywanie danych z klawiatury. dr hab. inż. Andrzej Czerepicki

TEMAT : KLASY DZIEDZICZENIE

Język programowania zbiór reguł określających, które ciągi symboli tworzą program komputerowy oraz jakie obliczenia opisuje ten program.

Opis: Instrukcja warunkowa Składnia: IF [NOT] warunek [AND [NOT] warunek] [OR [NOT] warunek].

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

Wykład 2 Składnia języka C# (cz. 1)

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

Programowanie w języku Python. Grażyna Koba

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Programowanie w Ruby

Python wprowadzenie. Warszawa, 24 marca PROGRAMOWANIE I SZKOLENIA

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Wykład 8: klasy cz. 4

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

LibreOffice Calc VBA

Mathcad c.d. - Macierze, wykresy 3D, rozwiązywanie równań, pochodne i całki, animacje

Zmienne, stałe i operatory

Oczywiście plik musi mieć rozszerzenie *.php

Tablice (jedno i wielowymiarowe), łańcuchy znaków

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

1 Podstawy c++ w pigułce.

Pascal - wprowadzenie

Laboratorium 3: Tablice, tablice znaków i funkcje operujące na ciągach znaków. dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

Podstawy programowania skrót z wykładów:

Wykład 5: Klasy cz. 3

Operacje wykonywane są na operandach (argumentach operatorów). Przy operacji dodawania: argumentami operatora dodawania + są dwa operandy 2 i 5.

Wyrażenie include(sciezka_do_pliku) pozwala na załadowanie (wnętrza) pliku do skryptu php. Plik ten może zawierać wszystko, co może się znaleźć w

Języki programowania zasady ich tworzenia

Język C++ zajęcia nr 2

JAVAScript w dokumentach HTML (1) JavaScript jest to interpretowany, zorientowany obiektowo, skryptowy język programowania.

Algorytmika i Programowanie VBA 1 - podstawy

Typy danych, cd. Łańcuchy znaków

PHP: bloki kodu, tablice, obiekty i formularze

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Lab 9 Podstawy Programowania

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

Podstawy Programowania C++

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

Wprowadzania liczb. Aby uniknąć wprowadzania ułamka jako daty, należy poprzedzać ułamki cyfrą 0 (zero); np.: wpisać 0 1/2

Podstawy bioinformatyki 2017/18

Podstawy programowania w C++

Wprowadzenie do języka Ruby

Programowanie w języku C++ Grażyna Koba

Programowanie w Ruby

Stałe definiuje się używając funkcji define. Przykład: define( PODATEK, 22); define( INSTALACJAOS, 70); define( MS, Microsoft );

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

Podstawy programowania. Podstawy C# Tablice

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Odczyt danych z klawiatury Operatory w Javie

Nazwa implementacji: Nauka języka Python wyrażenia warunkowe. Autor: Piotr Fiorek. Opis implementacji: Poznanie wyrażeń warunkowych if elif - else.

2. Łańcuchy tekstowe w PHP

Programowanie Obiektowe i C++

Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227 WYKŁAD 7 WSTĘP DO INFORMATYKI

IX. Wskaźniki.(3 godz.)

/* dołączenie pliku nagłówkowego zawierającego deklaracje symboli dla wykorzystywanego mikrokontrolera */ #include <aduc834.h>

Cw.12 JAVAScript w dokumentach HTML

MATERIAŁY DO ZAJĘĆ II

Wydział Zarządzania AGH. Katedra Informatyki Stosowanej. Podstawy VBA cz. 1. Programowanie komputerowe

Laboratorium Wstawianie skryptu na stroną: 2. Komentarze: 3. Deklaracja zmiennych

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Instrukcja do ćwiczeń nr 4 typy i rodzaje zmiennych w języku C dla AVR, oraz ich deklarowanie, oraz podstawowe operatory

Metaprogramowanie w Ruby

Programowanie Komputerów

Ogólny schemat prostego formularza: A może lepiej zamiast przycisku opartego o input tak:

1 Podstawy c++ w pigułce.

lekcja 8a Gry komputerowe MasterMind

Wykład 4. SQL praca z tabelami 1

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

Podstawy programowania. Wykład PASCAL. Zmienne wskaźnikowe i dynamiczne. dr Artur Bartoszewski - Podstawy prograowania, sem.

Po uruchomieniu programu nasza litera zostanie wyświetlona na ekranie

Właściwości i metody obiektu Comment Właściwości

Wydział Zarządzania AGH. Katedra Informatyki Stosowanej. Podstawy VBA cz. 2. Programowanie komputerowe

SWIFT. Zaawansowane Programowanie Obiektowe

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

1. Wprowadzenie do języka PHP

4. Funkcje. Przykłady

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

Wykład 3 Składnia języka C# (cz. 2)

Naukę zaczynamy od poznania interpretera. Interpreter uruchamiamy z konsoli poleceniem

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

W dowolnym momencie można zmienić typ wskaźnika.

Warto też w tym miejscu powiedzieć, że w C zero jest rozpoznawane jako fałsz, a wszystkie pozostałe wartości jako prawda.

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

Podstawy Programowania 2

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

Podstawowe części projektu w Javie

Wprowadzenie do programowania w języku Visual Basic. Podstawowe instrukcje języka

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Transkrypt:

Na podstawie: Programowanie w językach skryptowych wykład 5 Ruby Wykładów Marcina Młotkowskiego http://www.ii.uni.wroc.pl/~marcinm/dyd/ruby/ David Flanagan, Yukihiro Matsumoto, Ruby Programowanie https://www.ruby lang.org/pl/ 1

Tablice asocjacyjne Tablica asocjacyjna (ang. hash) to struktura danych przechowująca zbiór obiektów zwanych kluczami i skojarzone z nimi wartości. Tablice asocjacyjne nazywane są także mapami (ang. map), ponieważ odwzorowują klucze na wartości, oraz haszami. # Niniejsza tablica asocjacyjna kojarzy nazwy cyfr z cyframi. numbers = Hash.new # Utworzenie nowego pustego obiektu typu Hash. numbers["jeden"] = 1 # Powiązanie łańcucha "jeden" z obiektem typu Fixnum 1. numbers["dwa"] = 2 numbers["trzy"] = 3 # Użycie notacji tablicowej. sum = numbers["jeden"] + numbers["dwa"] # W ten sposób obiera się wartości.

Literały haszowe Literał haszowy ma postać listy par klucz wartość rozdzielonych przecinkami i zamkniętych w nawiasach klamrowych. Klucze i wartości są rozdzielane dwuznakowymi strzałkami =>. numbers = { "jeden" => 1, "dwa" => 2, "trzy" => 3 } Ogólnie jako klucze w tablicach asocjacyjnych efektywniejsze od łańcuchów są obiekty typu Symbol: numbers = { :jeden => 1, :dwa => 2, :trzy => 3 } Symbole to niemodyfikowalne łańcuchy wewnętrzne zapisywane jako identyfikatory z przedrostkiem w postaci dwukropka. Na końcu listy można wstawić przecinek: numbers = { :one => 1, :two => 2, } # Dodatkowy przecinek jest ignorowany. Kiedy klucze są symbolami, można używać bardzo zwięzłej składni: numbers = { jeden: 1, dwa: 2, trzy: 3 } Należy pamiętać, że pomiędzy identyfikatorem klucza a dwukropkiem nie może być spacji. 3

Kody mieszające, równość i klucze modyfikowalne Tablice asocjacyjne w języku Ruby są zaimplementowane za pomocą struktury danych o nazwie tablicy mieszającej (ang. hash table). Obiekty służące jako klucze w tablicy mieszającej muszą dysponować metodą o nazwie hash, która zwraca kod mieszający (ang. hashcode) typu Fixnum tych obiektów. Aby dwa klucze były identyczne, muszą mieć jednakowe kody mieszające. Nieidentyczne klucze również mogą mieć taki sam kod mieszający, ale tablice mieszające są najbardziej efektywne przy niewielkiej liczbie duplikatów kodów mieszających. Do porównywania kluczy w klasie Hash służy metoda eql?. W większości klas Ruby metoda eql? działa tak samo jak operator ==. Jeśli w nowo utworzonej klasie zostanie przesłonięta metoda eql?, konieczne jest też przesłonięcie metody hash. W przeciwnym wypadku egzemplarze tej klasy nie będą działać jak klucze w tablicach asocjacyjnych. Jeżeli w zdefiniowanej klasie nie zostanie przesłonięta metoda eql?, to jej obiekty używane jako klucze będą porównywane pod względem identyczności. Dwa różne egzemplarze klasy mają różne kody mieszające, nawet gdy zawierają taką samą treść. W takim przypadku właściwa jest domyślna metoda hash zwracająca unikatowy identyfikator object_id obiektu. Należy zauważyć, że obiekty modyfikowalne jako klucze mogą sprawiać problemy. Zmiana treści obiektu zazwyczaj pociąga za sobą zmianę jego kodu mieszającego. Jeśli obiekt użyty jako klucz zostanie zmodyfikowany, wewnętrzna tablica mieszająca zostanie uszkodzona i nie będzie działać poprawnie.

Łańcuchy w roli kluczy Ponieważ łańcuchy są modyfikowalne, ale powszechnie używa się ich w roli kluczy, Ruby traktuje je w wyjątkowy sposób, robiąc prywatną kopię każdego z nich. Jest to jednak specjalny przypadek. Używając innych typów modyfikowalnych obiektów jako kluczy, należy zachować szczególną ostrożność. Należy rozważyć tworzenie prywatnej kopii lub wywołanie metody freeze. Jeśli konieczne jest użycie modyfikowalnych obiektów jako kluczy, po każdej dokonanej w nich zmianie należy wywołać metodę rehash z klasy Hash. 5

Zakresy (Range) Obiekty klasy Range reprezentują wartości mieszczące się pomiędzy dwiema wartościami brzegowymi. Literał zakresowy składa się z dwóch wartości rozdzielonych dwiema lub trzema kropkami. Dwie kropki oznaczają, że zakres jest przedziałem zamkniętym; trzy kropki że zakres to przedział prawostronnie otwarty, a więc wartość końcowa nie wchodzi w jego skład: 1..10 # Liczby całkowite od 1 do 10, wliczając 10. 1.0...10.0 # Liczby pomiędzy 1.0, a 10.0, wyłączając 10.0. Do sprawdzenia, czy dana liczba należy do określonego przedziału, służy metoda include? cold_war = 1945..1989 cold_war.include? birthdate.year Z definicją zakresu związane jest pojęcie porządku. Jeśli zakres reprezentuje wartości między dwoma punktami brzegowymi, musi istnieć jakiś sposób na porównywanie ich z wartościami brzegowymi. W języku Ruby służy do tego operator <=> porównujący swoje operandy i zwracający wartość 1, 0 lub 1 w zależności od ich wzajemnego ułożenia (lub równości). Klasy takie jak liczbowe czy łańcuchowe, które są uporządkowane, definiują operator <=>. Wartość może zostać użyta jako brzeg zakresu tylko wówczas, gdy działa z tym operatorem. Punkty brzegowe zakresu i wartości w zakresie z reguły są tego samego typu. Jednak z technicznego punktu widzenia każda wartość zgodna z operatorami wartości brzegowych <=> może wchodzić w skład zakresu.

Zakresy c.d. Głównym celem zakresów jest porównywanie, czy dana wartość znajduje się w przedziale, czy poza nim. Drugim ważnym zastosowaniem jest iteracja. Jeśli klasa punktów brzegowych zakresu definiuje metodę succ (od ang. successor), istnieje dyskretny zbiór elementów zakresu, po których można iterować za pomocą metod each, step oraz metod z modułu Enumerable. Weźmy na przykład zakres 'a'..'c' : r = 'a'..'c' r.each { l print "[#{l}]" } # Drukuje "[a][b][c]". r.step(2) { l print "[#{l}]" } # Drukuje "[a][c]". r.to_a # => ['a','b','c']: Moduł Enumerable udostępnia metodę to_a. Kod ten działa, ponieważ klasa String definiuje metodę succ. Wynikiem operacji 'a'.succ jest 'b' a 'b'.succ jest 'c'. Zakresy, po których można iterować w ten sposób, to zakresy dyskretne. Zakresy, których punkty brzegowe nie definiują metody succ, nie umożliwiają iteracji, a więc można je nazwać zakresami ciągłymi. Zakresy, których punkty brzegowe to liczby całkowite, są dyskretne, natomiast zakresy z liczbami zmiennoprzecinkowymi na brzegach są ciągłe. 7

Wywołanie metody na rzecz zakresu 1..3.to_a # Wywołuje metodę to_a na rzecz liczby 3. (1..3).to_a # => [1,2,3]. Zakres musi być w nawiasach

Symbole Obiekt klasy Symbol wskazuje na symbol. Literał symbolu jest zbudowany z dwukropka poprzedzającego identyfikator: :symbol # Literał symbolu. :"symbol" # Ten sam literał. :'another long symbol' s = "string" sym = :"#{s}" # Symbol :string. # Cudzysowy pozwalaj na tworzenie symboli ze spacjami. Literały symboli obsługują także składnię %s, która pozwala na zastosowanie dowolnych ograniczników w podobny sposób jak %q i %Q w przypadku literałów łańcuchowych: %s["] # Tak samo jak :'"'. 9

Symbole w odwołaniach do nazw Symbole są często stosowane w odwołaniach do nazw metod w kodzie refleksyjnym. Załóżmy na przykład, że chcemy dowiedzieć się, czy jakiś obiekt implementuje metodę each: o.respond_to? :each Poniższa procedura sprawdza, czy dany obiekt obsługuje określoną metodę. Jeśli tak, wywołuje ją: name = :size if o.respond_to? name end o.send(name)

Konwersja String na Symbol Obiekt klasy String można przekonwertować na obiekt klasy Symbol, używając metody intern lub to_sym. Do wykonania operacji odwrotnej służy metoda o nazwie to_s, która posiada alias o nazwie id2name: str = "string" # Na początku jest łańcuch. sym = str.intern sym = str.to_sym # Konwersja na symbol. # Inny sposób na zrobienie tego samego. str = sym.to_s # Konwersja z powrotem na łańcuch. str = sym.id2name # Inny sposób na zrobienie tego samego. Dwa łańcuchy mogą mieć taką samą treść, a być dwoma całkiem odrębnymi obiektami. W przypadku symboli taka sytuacja nie może wystąpić. Dwa łańcuchy o takiej samej treści zostaną przekonwertowane na dokładnie ten sam obiekt klasy Symbol. Dwa różne obiekty klasy Symbol zawsze mają różną treść. 11

Łańcuch vs Symbol Pisząc kod, w którym łańcuchy są wykorzystywane nie ze względu na ich treść, ale jako rodzaj unikatowego identyfikatora, należy rozważyć użycie zamiast nich symboli. Na przykład zamiast pisać metodę, której argumentem musi być łańcuch "AM" lub "PM", można utworzyć metodę, której argumentem musi być symbol :AM lub :PM. Porównywanie dwóch obiektów klasy Symbol jest znacznie szybsze niż porównywanie łańcuchów. Dlatego generalnie jako klucze tablic asocjacyjnych preferowane są symbole, a nie łańcuchy.

Obiekty i referencje do obiektów Ruby jest czysto obiektowy. Wszystko jest obiektem pochodnym od klasy Object. Wykonując działania związane z obiektami w języku Ruby, w rzeczywistości działa się na referencjach do obiektów. Obróbce nie jest poddawany sam obiekt, a jego referencja. s = "Ruby" # Utworzenie obiektu klasy String. Zapisanie referencji do niego w zmiennej s. t = s # Skopiowanie referencji do zmiennej t. s i t wskazują ten sam obiekt. t[-1] = "" print s t = "Java" print s,t # Modyfikacja obiektu poprzez referencję. # Dostęp do zmodyfikowanego obiektu przez referencję. Drukuje "Rub". # t wskazuje teraz inny obiekt. # Drukuje "RubJava". Argumenty są przekazywane do metod przez wartość, a nie przez referencję, ale te przekazywane wartości są referencjami do obiektów. Ponieważ do metod przekazywane są referencje, metody mogą za ich pomocą modyfikować wskazywane przez nie obiekty. 13

Czas istnienia obiektu Wbudowane klasy Ruby opisane w niniejszym rozdziale posiadają składnie literałowe, a ich egzemplarze są tworzone poprzez wpisanie ich wartości w kodzie. Obiekty innych klas muszą być tworzone jawnie. Najczęściej używa się w tym celu metody new : myobject = myclass.new new jest metodą klasy Class. Przydziela pamięć dla nowego obiektu, a następnie inicjuje jego stan za pomocą wywołania metody initialize. Argumenty metody new są bezpośrednio przekazywane do metody initialize. Większość klas udostępnia initialize wykonującą wszelkie czynności inicjacyjne niezbędne przy tworzeniu ich egzemplarzy. Metody new i initialize są domyślnym sposobem tworzenia nowych obiektów, ale klasa może udostępniać także inne metody zwracające jej egzemplarze nazywane metodami fabrykującymi (fabrykami). Ruby stosuje garbage collector do niszczenia obiektów. Obiekt staje się kandydatem do zniszczenia, kiedy jest nieosiągalny, to znaczy gdy w żadnym osiągalnym obiekcie nie ma do niego referencji.

Identyfikator obiektu oraz klasa i typ obiektu Każdy obiekt posiada identyfikator liczbę typu Fixnum który można sprawdzić za pomocą metody object_id. Wartość zwrócona przez tę metodę jest stałą unikatową przez cały cykl życia obiektu. Dopóki obiekt jest osiągalny, cały czas ma ten sam identyfikator i żaden inny obiekt nie będzie mógł mieć takiego samego identyfikatora. Sprawdzenie klasy obiektu: o = "test" # To jest wartość o.class # Zwraca obiekt reprezentujący klasę string. Można także sprawdzić, jaka klasa jest nadklasą klasy dowolnego obiektu: o.class # String: o jest obiektem klasy String. o.class.superclass # Object: nadklasy klasy String jest klasą Object. o.class.superclass.superclass # nil: klasa Object nie ma nadklasy w Ruby 1.8 Od Ruby 1.9 klasa Object nie jest już klasą najwyższego poziomu: Object.superclass # BasicObject: klasa Object ma nadklasę w Ruby 1.9. BasicObject.superclass # nil: klasa BasicObject nie ma nadklasy. 15

Klasa i typ obiektu c.d. Najprostszym sposobem na sprawdzenie klasy obiektu jest bezpośrednie porównanie: o.class == String # true, jeżeli nie należy do klasy String. To samo: o.instance_of? String # true, jeżeli należy do klasy String. Sprawdzanie czy obiekt jest egzemplarzem jakiejś podklasy tej klasy. Należy użyć metody is_a? lub jej synonimu kind_of?: x = 1 x.instance_of? Fixnum # true: egzemplarz klasy Fixnum. x.instance_of? Numeric # false: metoda instance_of? nie sprawdza w gb hierarchii. x.is_a? Fixnum # true: x należy do klasy Fixnum. x.is_a? Integer # true: x należy do klasy Integer. x.is_a? Numeric # true: x należy do klasy Numeric. x.is_a? Comparable # true: działa także z modułami domieszkowymi. x.is_a? Object # true dla każdej wartości x.

Klasa Class definiuje operator === który jest synonimem is_a?: Numeric === x # true: x jest Numeric. 17

Typ obiektu Mówiąc typ obiektu, mamy na myśli zestaw działań charakteryzujących ten obiekt. Innymi słowy, typ obiektu to zestaw metod, które można wywoływać na jego rzecz (definicja ta jest nieprecyzyjna, ponieważ znaczenie mają nie tyle nazwy metod, co typy argumentów, które te metody mogą przyjmować). Dla programisty ważne jest, czy można na rzecz obiektu wywołać określone metody. o.respond_to? :"<<" # true, jeśli o posiada operator <<.

Porównywanie obiektów (equal) Metoda equal? jest zdefiniowana w klasie Object. Sprawdza, czy dwie wartości odwołują się do dokładnie tego samego obiektu. Dla dowolnych dwóch różnych obiektów metoda ta zawsze zwraca wartość false : a = "Ruby" # Jedna referencja do jednego obiektu klasy String. b = c = "Ruby" # Dwie referencje do innego obiektu klasy String. a.equal?(b) # false: a i b są różnymi obiektami. b.equal?(c) # true: b i c odwołują się do tego samego obiektu. Zgodnie z konwencją metoda equal? nigdy nie jest przesłaniana w podklasach. Innym sposobem na sprawdzenie, czy dwie referencje odwołują się do tego samego obiektu: a.object_id == b.object_id # Działa jak wywołanie a.equal?(b). 19

Operator == Operator == jest najczęściej używanym sposobem na porównanie obiektów. Sprawdza, czy dwie referencje do obiektu są identyczne. Większość klas przedefiniowuje ten operator, aby umożliwić porównywanie odrębnych egzemplarzy: a = "Ruby" b = "Ruby" a.equal?(b) # Obiekt klasy String. # Inny obiekt klasy String z taką samą treścią. # false: a i b nie odwołują się do tego samego obiektu. a == b # true: ale oba te odrębne obiekty mają identyczne wartości.

Jawna konwersja obiektów Metody konwersji jawnej są używane w kodzie aplikacji do zamiany wartości jednej klasy na wartość innej klasy. Do najczęściej używanych metod w tej kategorii należą: to_s, to_i, to_f i to_a, które konwertują obiekty na klasy odpowiednio: String, Integer, Float i Array. Standardowe metody z reguły nie wywołują tych metod automatycznie. Jeśli do wywołania metody przyjmującej jako argument obiekt klasy String zostanie przekazany obiekt innej klasy, metoda ta nie wywoła automatycznie metody to_s, aby dokonać konwersji (natomiast wartości interpolowane do łańcuchów w podwójnych cudzysłowach są automatycznie konwertowane za pomocą metody to_s). Metoda to_s jest najważniejszą ze wszystkich metod konwertujących, ponieważ obiekty łań cuchowe są powszechnie stosowane w interfejsach użytkownika. 21

Niejawna konwersja obiektów to_str konwertuje obiekt na obiekt String. to_int konwertuje obiekty na podobieństwo liczb całkowitych, to_ary obiekty podobne do tablic to_hash obiekty podobne do tablic asocjacyjnych. Niestety, warunki, w których te metody są wywoływane, nie są dobrze opisane w dokumentacji. Ponadto metody te nie są zaimplementowane w wielu standardowych klasach.

Funkcje konwertujące Moduł Kernel definiuje cztery metody konwertujące: Array, Float, Integer, String Nazwy reprezentują klasy na które konwertują. Funkcja Array próbuje konwertować swój argument na tablicę, wywołując metodę to_ary. Jeśli metoda nie została zdefiniowana lub zwraca wartość nil, następuje próba wywołania metody to_a. Jeżeli metoda to_a nie została zdefiniowana lub zwraca wartość nil, funkcja Array zwraca nową tablicę, której jedynym elementem jest argument tej funkcji. Funkcja Float konwertuje argumenty klasy Numeric bezpośrednio na klasę Float. Dla każdej wartości innej klasy niż Numeric wywołuje metodę to_f. Funkcja String konwertuje swój argument na łańcuch, wywołując na jego rzecz metodę to_s 23

Funkcje konwertujące c.d. Funkcja Integer konwertuje swój argument na klasę Fixnum lub Bignum. Jeśli argument jest wartością klasy Numeric, następuje bezpośrednia konwersja. Wartości zmiennoprzecinkowe nie są zaokrąglane, usuwa się tylko ich część ułamkową. W sytuacji gdy argument jest łańcuchem, funkcja ta szuka informacji o podstawie systemu liczenia (wiodące zero oznacza liczbę ósemkową, 0x szesnastkową, a 0b binarną) i odpowiednio konwertuje ten łańcuch. W przeciwieństwie do metody String.to_i funkcja ta nie pozwala na stosowanie na końcu znaków innych niż cyfry. W przypadku argumentów każdego innego typu funkcja Integer najpierw próbuje konwersji za pomocą metody to_int, a później to_i.

Zamrażanie obiektów Każdy obiekt można zamrozić za pomocą metody freeze. Zamrożony obiekt nie może zostać zmodyfikowany żaden z jego wewnętrznych stanów nie może się zmienić, a wywołanie którejkolwiek z jego metod mutacyjnych kończy się niepowodzeniem: s = "lód" # Łańcuchy są obiektami modyfikowalnymi. s.freeze # Zamrożenie łańcucha, aby był niemodyfikowalny. s.frozen? # true: obiekt został zamrożony. s.upcase! # TypeError: nie można zmodyfikować zamrożonego łańcucha. s[0] = "mi" # TypeError: nie można zmodyfikować zamrożonego łańcucha. Zamrożenie obiektu klasowego uniemożliwia dodawanie metod do jego klasy. Do sprawdzania, czy obiekt jest zamrożony, służy metoda frozen?. Po zamrożeniu obiektu nie ma sposobu na jego rozmrożenie. Jeśli obiekt zostanie skopiowany za pomocą metody clone, kopia również będzie zamrożona. Natomiast kopia wykonana przy użyciu metody dup nie będzie zamrożona. 25

Zanieczyszczanie obiektów Aplikacje sieciowe często muszą badać dane otrzymywane od niezaufanych użytkowników w celu uniknięcia ataków typu SQL injection lub innych tego rodzaju zagrożeń. Każdy obiekt można oznaczyć jako zanieczyszczony za pomocą metody taint. Wszystkie obiekty pochodzące od zanieczyszczonego obiektu również są zanieczyszczone. Aby sprawdzić, czy obiekt jest zanieczyszczony, należy użyć metody tainted? : s = "niezaufany" # Domyślnie obiekty nie są zanieczyszczone. s.taint # Oznaczenie niezaufanego obiektu jako zanieczyszczony. s.tainted? # true: obiekt jest zanieczyszczony. s.upcase.tainted? # true: obiekty pochodne są zanieczyszczone. s[3,4].tainted? # true: podłańcuchy są zanieczyszczone. Dane pochodzące od użytkownika takie jak argumenty wiersza poleceń, zmienne środowiskowe oraz łańcuchy wczytywane przez metodę gets są automatycznie oznaczane jako zanieczyszczone. Kopie obiektów zanieczyszczonych robione za pomocą metod clone i dup pozostają zanieczyszczone. Obiekt zanieczyszczony można oczyścić, stosując metodę untaint. Oczywiście należy to robić wyłącznie wówczas, gdy po zbadaniu obiektu ma się pewność, że jest on bezpieczny.

Zanieczyszczanie obiektów c.d. Możliwości mechanizmu zanieczyszczania obiektów w języku Ruby można w pełni wykorzystać w połączeniu ze zmienną globalną $SAFE. Jeśli zmienna ta jest ustawiona na wartość większą od zera, Ruby nie zezwoli niektórym standardowym metodom na działanie z obiektami zanieczyszczonymi. 27

Zmienne niezainicjowane Z reguły zanim użyje się zmiennej w wyrażeniu, zawsze powinno się najpierw nadać jej wartość, czyli zainicjować ją. Są jednak sytuacje, w których Ruby pozwala na używanie niezainicjowanych zmiennych. Zasady dotyczące tej kwestii są inne dla różnych rodzajów zmiennych. Zmienne klasowe Zmienne klasowe zawsze muszą mieć przypisaną wartość, aby mogły zostać użyte. Odwołanie do zmiennej klasowej bez przypisanej wartości powoduje zgłoszenie przez Ruby błędu NameError. Zmienne obiektowe Odwołanie do niezainicjowanej zmiennej obiektowej powoduje zwrot wartości nil. Jednak poleganie na tym zachowaniu jest uznawane za zły styl programowania. Ruby zgłasza ostrzeżenie o takiej niezainicjowanej zmiennej, jeśli zostanie uruchomiony z opcją w

Zmienne niezainicjowane c.d. Zmienne globalne Niezainicjowane zmienne globalne są jak niezainicjowane zmienne obiektowe dają wartość nil, ale powodują zgłoszenie ostrzeżenia, jeśli Ruby zostanie uruchomiony z opcją w. Zmienne lokalne Ten przypadek jest bardziej skomplikowany niż poprzednie, ponieważ przed zmiennymi lokalnymi nie ma żadnego prefiksu w postaci znaku interpunkcyjnego. Oznacza to, że odwołania do zmiennych lokalnych wyglądają tak samo jak wywołania metod. Jeśli interpreter Ruby spotkał wcześniej przypisanie wartości do zmiennej lokalnej, wie, że nie jest to metoda tylko zmienna, dzięki czemu może zwrócić wartość tej zmiennej. Jeżeli nie było żadnego przypisania, Ruby traktuje takie wyrażenie jako wywołanie metody. W przypadku gdy nie istnieje metoda o takiej nazwie, zgłaszany jest błąd NameError. 29

Stałe Stała w języku Ruby jest prawie tym samym co zmienna, z tym wyjątkiem, że jej wartość nie może się zmienić do końca działania programu. Interpreter Ruby nie wymusza, aby stałe nie zmieniały wartości, ale kiedy tak się stanie, zgłasza ostrzeżenie. Z leksykalnego punktu widzenia nazwy stałych wyglądają tak samo jak nazwy zmiennych, ale zaczynają się od wielkich liter. Zgodnie z konwencją nazwy większości stałych są pisane w całości wielkimi literami, a do rozdzielania poszczególnych słów służą znaki podkreślenia JAK_TUTAJ. Nazwy klas i modułów są także stałymi, jednak konwencja nakazuje, aby tylko pierwsza litera każdego wyrazu składowego była wielka JakTutaj. Mimo że stałe wyglądają jak zmienne lokalne z wielkimi literami, mają zasięg zmiennych globalnych można ich używać w dowolnym miejscu programu. Jednak w przeciwieństwie do zmiennych globalnych stałe mogą być definiowane w klasach i modułach, a więc mogą posiadać nazwy kwalifikowane. Odwołanie do stałej jest wyrażeniem, którego wartością jest wartość tej stałej. Najprostsze odwołania do stałych są wyrażeniami pierwotnymi składają się tylko z nazwy stałej: CM_PER_INCH = 2.54 # Definicja stałej. CM_PER_INCH # Odwołanie do stałej. Wartość 2.54.

Złożone odwołania do stałych Conversions::CM_PER_INCH # Stała zdefiniowana w module Conversions. modules[0]::name # Stała zdefiniowana przez element tablicy. Moduły można zagnieżdżać, co oznacza, że stałe mogą być definiowane w zagnieżdżonych przestrzeniach nazw jak poniżej: Conversions::Area::HECTARES_PER_ACRE Prawą stronę :: można pominąć. Wtedy stałej poszukuje się w zakresie globalnym: ::ARGV # Staa globalna ARGV. 31

Wywołania metod Wyrażenie wywołania metody składa się z czterech części: Dowolnego wyrażenia, którego wartością jest obiekt, na rzecz którego ma być wywołana metoda. Wyrażenie to jest oddzielone od nazwy metody separatorem. lub ::. Wyrażenie i separator są opcjonalne. Jeśli zostaną pominięte, metoda jest wywoływana na rzecz obiektu wyznaczonego przez słowo kluczowe self. Nazwy wywoływanej metody. Jest to jedyna wymagana część wyrażenia wywołania metody. Argumentów przekazywanych do metody. Lista argumentów może znajdować się w nawiasach, ale ich stosowanie jest zazwyczaj nieobowiązkowe. Argumenty rozdzielane są przecinkami. Liczba i typ wymaganych argumentów zależy od definicji metody. Niektóre metody nie przyjmują żadnych argumentów. Opcjonalnego bloku kodu otoczonego klamrami lub parą słów do i end. Metoda może wywołać ten kod za pomocą instrukcji yield. Wartością wyrażenia wywołania metody jest wartość ostatniego z wyrażeń w ciele tej metody.

Przykłady wywołań metod puts "witaj" # Metoda puts wywołana na rzecz self z jednym argumentem łańcuchowym. Math.sqrt(2) # Metoda sqrt wywołana na rzecz obiektu Math z jednym argumentem. message.length # Metoda length wywołana na rzecz obiektu message; brak argumentów a.each { x p x } # Metoda each wywołana na rzecz obiektu a z dołączonym blokiem. 33

Przypisania x,y,z = 1,2,3 # Ustawienie x na 1, y na 2 i z na 3. x,y = y,x # Równoległa zamiana wartości dwóch zmiennych. x = y; y = x # Sekwencyjne obie zmienne maj tę samą wartość x = 1, 2, 3 # x = [1,2,3]. x, = 1, 2, 3 # x = 1; pozostałe wartości są odrzucane. x, y, z = [1, 2, 3] # To samo co x,y,z = 1,2,3. x, y, z = 1, 2 # x=1; y=2; z=nil. x, y = 1, 2, 3 # x=1; y=2; 3 nie zostaje przypisana nigdzie. class Ambiguous def x; 1; end # Metoda o nazwie x. Zawsze zwraca wartość 1. def test puts x # Nie znaleziono żadnej zmiennej; odwołuje się do powyższej metody i drukuje 1. # Wartość poniższego wiersza kodu nie jest nigdy obliczana ze względu na klauzulę if false. # Jednak interpreter widzi go i traktuje x jako zmienną w pozostałej części tej metody. x = 0 if false puts x # x jest zmienną, ale nigdy nie przypisano jej wartości drukuje nil. x = 2 # Niniejsze przypisanie zostanie obliczone. puts x # Dlatego niniejsza instrukcja wydrukuje 2. end end

Przypisanie do atrybutów obiektu Definicja metod m= i m. o.m = v Interpreter Ruby konwertuje to przypisanie na poniższe wywołanie metody: o.m=(v) # Jeżeli zostaną opuszczone nawiasy i dodana spacja, wyrażenie to będzie wygldało # jak przypisanie! To znaczy że wartość v zostaje przekazana do metody m=, która może wykonać dowolne działanie na tej wartości. Z reguły jest to sprawdzenie, czy wartość jest odpowiedniego typu i mieści się w odpowiednim zakresie, a następnie zapisanie w zmiennej obiektowej obiektu. Metodom typu m= zazwyczaj towarzyszy metoda m, która zwraca ostatnią wartość przekazaną do m=. Metody typu m= nazywane są metodami ustawiającymi (settery), a metody typu m metodami dostępowymi (gettry). Kiedy obiekt udostępnia obie te metody, mówi się, że ma atrybut m. 35

Potęgowanie Operator ** wykonuje potęgowanie, czyli podnosi swój pierwszy argument do potęgi określonej drugim argumentem. Wstawiając w miejsce drugiego operandu ułamek, można obliczyć pierwiastek z pierwszego operandu. Na przykład pierwiastek trzeciego stopnia z x wynosi x**(1.0/3.0). Podobnie x** y jest równoważne z 1/(x**y). 37

Operatory +,, *, / % W klasie String operator + wykonuje konkatenację, * powtarza łańcuchy, a % służy do zamiany argumentów metody sprintf na łańcuchy. W klasie Array operator + jest używany do konkatenacji, a do odejmowania tablic. Operator * działa na różne sposoby w zależności od klasy drugiego operandu. Kiedy tablica jest mnożona przez liczbę, wynikiem jest nowa tablica zawierająca zawartość oryginalnej tablicy powtórzoną określoną liczbę razy. Jeśli jednak tablica jest mnożona przez łańcuch, wynik jest taki sam jak przy wywołaniu metody join tej tablicy i przekazaniu łańcucha jako argumentu.

Przesunięcie i dołączenie: << i >> (0b1011 << 1).to_s(2) # => "10110" 11 << 1 => 22. (0b10110 >> 2).to_s(2) # => "101" 22 >> 2 => 5. Operator << jest także używany do dołączania i w tej funkcji jest prawdopodobnie częściej stosowany. W ten sposób jest on zdefiniowany między innymi w klasach String, Array, IO, Queue: message = "witaj" messages = [] # Pusta tablica. message << " wiecie" # Dołączenie do łańcucha. messages << message # Dołączenie komunikatu do tablicy. STDOUT << message # Wydruk komunikatu w standardowym strumieniu #wyjściowym. 39

Operator <=> Klasy mogą definiować każdy z operatorów porównywania osobno. Jednak łatwiej (i częściej się to robi) jest zdefiniować jeden operator porównywania <=>. Jest on przeznaczony do celów ogólnych, a jego wartość zwrotna określa względną kolejność dwóch operandów. Jeśli operand z lewej strony jest mniejszy od operandu z prawej strony, <=> zwraca wartość 1. W sytuacji gdy operand z lewej strony jest większy, zwracana jest wartość +1. Gdy operandy są równe, wartością zwrotną jest 0. Jeżeli operandy nie dadzą się porównać, zwracana jest wartość nil. Kiedy zdefiniowany jest operator <=>, klasa może zawierać moduł Comparable, który definiuje pozostałe operatory porównywania (w tym operator ==) w kategoriach operatora <=>.

Operatory <> w klasie Module Operatory porównywania w tej klasie określają powiązania podklas (klasa Module jest nadklasą klasy Class). A < B ma wartość true dla klas A i B, jeśli A jest podklasą, czyli potomkiem klasy B. W tym przypadku mniejszy niż oznacza bardziej wyspecjalizowany, czyli o węższym typie. Dodatkowo warto zapamiętać, że znak < jest używany także przy deklarowaniu podklas. # Deklaracja klasy A jako podklasy klasy B. class A < B end Operator > w klasie Module działa tak samo jak < po zamianie miejscami operandów. Ponadto operatory <= i >= zwracają wartość true, jeśli oba operandy są tej samej klasy. Najbardziej interesującą rzeczą w operatorach porównywania klasy Module jest to, że definiuje ona tylko częściowy porządek swoich wartości. Biorąc pod uwagę klasy String i Numeric, obie są podklasami klasy Object i żadna nie jest podklasą tej drugiej. W przypadku kiedy operandy nie są ze sobą powiązane, operatory porównywania zwracają wartość nil: String < Object # true: Klasa String jest bardziej wyspecjalizowana niż #Oject. Object > Numeric # true: Klasa Object jest bardziej ogólna ni klasa Numeric. Numeric < Integer # false: Klasa Numeric nie jest bardziej wyspecjalizowana od klasy Integer. String < Numeric # nil: Klasy String i Numeric nie są powiązane ze sobą. 41

Przerzutniki logiczne Operatory.. i... użyte w instrukcjach warunkowych jak if lub w pętlach jak while nie tworzą obiektów klasy Range. W zamian zwracają specjalny rodzaj wyrażenia logicznego o nazwie przerzutnik (ang. flip flop). Wartością wyrażenia przerzutnikowego, podobnie jak wyrażeń porównujących, jest true lub false. Wyjątkowość wyrażenia przerzutnikowego polega na tym, że jego wartość zależy od wartości wcześniejszych obliczeń. Oznacza to, że wyrażenie przerzutnikowe ma określony stan musi pamiętać informacje o poprzednich obliczeniach. Ze względu na posiadanie stanu można się spodziewać, iż wyrażenie przerzutnikowe jest jakimś rodzajem obiektu. Tak jednak nie jest jest to wyrażenie języka Ruby, a stan (pojedyncza wartość logiczna) jest przechowywany przez interpreter w wewnętrznej przetworzonej reprezentacji tego wyrażenia. Pierwszy operator.. zwraca obiekt klasy Range. Drugi tworzy wyrażenie przerzutnikowe: (1..10).each { x print x if x==3..x==5}

Przerzutnik.. vs Zarówno operator.., jak i... mogą być używane w przerzutnikach. Różnica polega na tym, że kiedy przerzutnik utworzony za pomocą operatora.. zmienia stan na true, zwraca wartość true, ale dodatkowo sprawdza wartość wyrażenia po prawej stronie, aby sprawdzić, czy nie powinien zmienić swojego stanu z powrotem na false. Wersja z operatorem... sprawdza wyrażenie po prawej stronie dopiero przy kolejnym obliczaniu. # Drukuje "3". Zmienia stan i wraca do pierwotnego stanu, kiedy x==3. (1..10).each { x print x if x == 3..x >= 3} # Drukuje "34". Zmienia stan, kiedy x == 3, i wraca do pierwotnego, kiedy x==4. (1..10).each { x print x if x == 3...x >= 3} # Drukuje "34". 43

Funkcja symulująca działanie przerzutnika: $state = false # Globalna zmienna przechowująca stan przerzutnika. def flipflop(x) # Sprawdzenie wartości zmiennej x względem przerzutnika. if!$state # Jeżeli zapisany stan to false, result = (x == 3) # wynikiem jest wartość lewego operandu. if result # Jeżeli wynik ten to true, $state =!(x == 5) # to zapisany stan nie pochodzi od prawego operandu. end result # Zwrot wyniku. else # W przeciwnym przypadku, jeżeli zapisany stan to true, $state =!(x == 5) # zapisanie odwrotności prawego operandu true # i zwrot wartości true bez #sprawdzania wartości lewego operandu. end end Mając zdefiniowaną powyższą funkcję przerzutnika, można napisać poniższy kod, który podobnie jak wcześniejszy przykład drukuje 345: (1..10).each { x print x if flipflop(x)}