W Rubim występują trzy podstawowe typu służące do reprezentacji liczb
Fixnum
Bignum
Float
Pierwsze dwa typy służą do reprezentacji liczba całkowitych, a trzeci do reprezentacji liczb zmiennopozycyjnych. Różnica pomiędzy dwoma pierwszymi typami polega na tym, że w typie Bignum
są reprezentowane liczby, które nie mogą być przechowywane natywnie na wykorzystywanej architekturze sprzętowej. O ile zatem nie reprezentujesz bardzo dużych liczb, nie musisz za bardzo przejmować się tym rozróżnieniem.
Ważniejsze są różnice między typami Fixnum
oraz Float
.
Aby stworzyć liczbę typu Fixnum
po prostu wpisujemy jej wartość w reprezentacji dziesiętnej, np.
5
Typ dowolnej zmiennej można poznać za pomocą wywołania class
5.class
Aby stworzyć liczbę typu Float
, wartość liczby musi zawierać kropkę, np.
5.0
5.0.class
Możliwe jest dodawanie liczb różnych typów - następuje wtedy automatyczna kowersja
a = 5
b = 10.0
c = a + b
c.class
Na obu typach liczb można wykonywać następujące operacje arytmetyczne:
Operator | Działanie |
---|---|
+ |
dodawanie |
- |
odejmowanie |
* |
mnożenie |
/ |
dzielenie |
% |
dzielenie modulo |
** |
potęgowanie |
Przykładowo
(10**2 + 2 * 3) % 6
Możliwe jest również wykonywanie bardziej złożonych operacji. Znajdują się one w module Math
.
Jeśli chcemy np. wyciągnąć pierwiastek kwadratowy z liczby to piszemy
Math.sqrt(2)
A logarytm naturalny to Math.log
Math.log(2)
Ważne zastrzeżenie dotyczy operatora dzielenia - działa o inaczej dla liczb całkowitych, a inaczej dla zmiennopozycyjnych. W przypadku tych pierwszych wynik jest liczbą całkowitą, więc mamy do czynienia z dzieleniem modulo:
10 / 3
W przypadku tych drugich mamy do czynienia z dzieleniem zwykływ - wystarczy aby tylko jeden argument był liczbą zmiennopozycyjną, aby całe wyrażenie było traktowane jak dzielenie zwykłe
10.0 / 3
Możliwe jest również dokonywanie kowersji pomiędzy tymi typami:
5.5.to_i
5.to_f
Warto zwrócić uwagę, że zamiana na liczbę całkowitą powoduje obcięcie wartości ułamkowej, bez zaokrąglenia:
5.6.to_i
Jeśli chcemy zaokrąglić wartość, musimy najpierw użyć wywołania round
5.6.round.to_i
Choć wydawać się może, że operacja na liczbach nie są wywołaniami metod, to w rzeczywistości nie jest to prawda. Każde wyrażenie artymetyczne może być zamienione na swoją postać obiektową:
5 + 2 * 3
to to samo co
5.+(2.*(3))
Ważne jest jednak to, że operatory artymetyczne mają swoje naturalne priorytety, dlatego nie musimy obawiać się, że wszystkie wyrażenia będą ewaluowane od lewej do prawej, jak to ma miejsce w przypadku zwykłego wywoływania metod.
Poza najprostszymi operacjami arytmetycznymi, na liczbach można wywoływać wiele metod. Wcześniej widzieliśmy już metody round
, to_i
oraz to_f
. Inne ciekawe metody to zero?
0.zero?
1.zero?
between?
15.between(10,20)
abs
-1.abs
succ
7.succ
Oraz times
3.times{ print "Jestem kometą. " }
Istnieją biblioteki, które dodają bardziej zaawansowane metody do obiektów liczbowych. Np. biblioteka używana ActiveSupport
w Railsach pozwala na wykonywanie działań arytmetycznych na datach
require 'active_support/time'
3.days.ago
Oblicz pierwsiatek trzeciego stopnia z 10. Wynik przedstaw w zaokrągleniu do dwóch miejsc po przecinku.
Symbole w Rubim reprezentują nazwy. Symbole tworzone są poprzez umieszczenie dwukropka na początku nazwy:
:name
Symboli nie należy mylić z napisami. Te drugie wykorzystywane są do reprezentowania danych tekstowych, np. pochodzących od użytkownika. Napisy mogą być konkatenowane, można sprawdzać ich długość, etc. Tych operacji nie można wykonywać na symbolach. Ponadto napisy są modyfikowalne a symbole nie.
Symbole są nazwami wykorzystywanymi wewnątrz programu. Z tego względu, w przeciwieństwie do napisów, dwa symbole, które wyglądają identycznie są zawsze tym samym symbolem (mają ten sam identyfikator).
:name.equal?(:name)
"name".equal?("name")
Zakresy to inny ciekawy element języka Ruby. Stosowane są one do reprezentowania przedziałów wartości. Wyróżnia się dwa typy zakresów - obustronnie domknięte, w których występują dwie kropki:
(1..5)
oraz lewostronnie domknięte, w których występują 3 kropki:
(1...5)
Różnica pomiędzy nimi polega na tym, że pierwszy zakres obejmuje wszystkie wymienione wartości, a drugi zakres nie obejmuje wartości stojącej po prawej stronie. Najłatwiej można się o tym przekonać, sprawdzając, czy jakaś wartość należy do zakresu za pomocą metody include?
:
(1..5).include?(5)
(1...5).include?(5)
Istnieją dwie podstawowe metody pozwalające na sprawdzenie kresów zakresu: min
oraz max
:
(1..5).min
(1..5).max
(1...5).max
Zakresy są często wykorzystywane w różnych wywołaniach, w szczególności tych związanych ze strukturami sekwencyjnymi, np. napisami. Wywołując na napisie metodę []
w istocie korzystaliśmy z zakresów:
"Ala ma kota"[0..3]
Ważną cechą zakresów jest to, że zajmują one znacznie mniej pamięci niż np. tablice zawierające wszystkie elementy należące do zakresu. Możliwe jest jednak jawne przekształcenie zakresów w tablice, za pomocą wywołania to_a
:
(1..5).to_a
Ostatnią ważną cechą zakresów jest to, jak definiują one operator ===
. Operator ten zwróci wartość prawdy, jeśli wartośc po prawej stronie należy do zakresu.
(1..5) === 3
(1..5) === 7
Dlatego właśnie możliwe jest wykorzystanie zakresów w instrukcji case
:
year = 1990
case year
when 1901..2000
"XX wiek"
when 2001..2100
"XXI wiek"
else
"ciemne wieki"
end
Korzystając z poprzedniego przykładu zdefiniuj algorytm, który będzie zamieniał wartości liczbowe w notacji arabskiej na notację rzymską w przedziale od 1 do 10.