Przedziały, liczby i wstęp do matematyki w Rubim
Posted by Jacek Galanciak on May 13 2007
Ruby w swoim standardowym wyposażeniu posiada bardzo dobre wsparcie dla matematyki, które wychodzi daleko poza standardowe funkcje, które znamy z modułów math
innych języków programowania. Odkryjmy więc na nowo piękno matematyki - z Rubim.
Przedziały
Przedział jest obiektem standardowej klasy Range
. Możemy je tworzyć na dwa sposoby:
a = 1..9 # => 1..9
b = 1...9 # => 1...9
Różnica między nimi jest taka, że dwie kropki tworzą przedział od-do prawostronnie domknięty, a trzy kropki - otwarty. Przekonajmy się, że to prawda:
a.to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9]
b.to_a # => [1, 2, 3, 4, 5, 6, 7, 8]
Przedziały tworzymy tak, że wartość mniejsza musi byc zawsze po lewej stronie:
(-1..2).to_a # [-1, 0, 1, 2]
(2..-1).to_a # []
Nic nie stoi na przeszkodzie, byśmy tworzyli przedziały ograniczone liczbami rzeczywistymi:
-2.5..4 # -2.5..4
Mechanika konwersji przedziałów nie pozwala na stworzenie tablicy z przedziału liczb zmiennoprzecinkowych - nieprawidłowe jest wywołanie (-2.5..4).to_a
Przedziały nieliczbowe
Ruby pozwala nam na tworzenie przedziałów nie tylko z liczb, ale i znaków i Stringów:
('a'..'z').to_a # ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
# "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
# "u", "v", "w", "x", "y", "z"]
("aa".."bf").to_a # ["aa", "ab", "ac", "ad", "ae", "af", "ag", "ah",
# "ai", "aj", "ak", "al", "am", "an", "ao", "ap",
# "aq", "ar", "as", "at", "au", "av", "aw", "ax",
# "ay", "az", "ba", "bb", "bc", "bd", "be", "bf"]
Przykłady
r1 = 'a'..'z' # => "a".."z"
r2 = 1..10 # => 1..10
r3 = 1...10 # => 1...10
r1.min # => "a"
r2.max # => 10
r3.max # => 9
r3.end # => 10
r1.include? 'c' # => true
r2.include? 5 # => true
r2 === 5 # => true
r2.include? 12 # => false
r1.each { |c| print c } # => "a".."z"
# Wyjście: abcdefghijklmnopqrstuvwxyz
Przedziały logiczne
Poświęciłem im cały wpis. Warto zajrzeć, gdyż konstrukcja posiada całkiem ciekawe zastosowania.
Liczby w Rubim
Język Ruby obsługuje wszystkie mniej lub bardziej standardowe typy liczbowe. Prześledźmy parę przykładów:
a = 5 # => 5
a.class # => Fixnum
a/2 # => 2
a%2 # => 1
b = a/2.0 # => 2.5
b.class # => float
b.round # => 3
b.floor # => 2
b.ceil # => 3
(-1.25).abs # => 1.25
1.zero? # => false
0.zero? # => true
3.between? 1, 5 # => true
3.between? 6, 10 # => false
# Potęgujemy
2**16 # => 65536
# Konwersje
5.to_s # => "5"
(-2.4).to_s # => "-2.4"
"-2.4".to_i # => "-2"
"-2.4".to_f # => "-2.4"
# Największy wspólny dzielnik
120.gcd 72 # => 24
# Najmniejsza wspólna wielokrotność
6.lcm 9 # => 18
# Jedno i drugie:
6.gcdlcm 9 # => [3, 18]
# Sposoby zapisu liczb
# Dziewięc milionów sto dwadzieścia pięć tysięcy pięćset siedemnaście
a = 9125517 # => 9125517
# Da się czytelniej:
a = 9_125_517
# Różne systemy liczbowe
a = 0x6c # => 108
b = 0o72 # => 58
b = 072 # => 58
c = 0b10011001 # => 153
Dzielimy przez zero
Dobrze widzisz. Przez zero. O ile działanie to na typach całkowitych jest niewykonalne, o tyle na floatach daje całkiem “normalny” wynik:
a = 1.0/0 # => Infinity
a.class # => float
a.infinite? # => 1
(-1.0/0).infinite? # => -1
a + 5 # => Infinity
a/5 # => Infinity
1/a # => 0
a/a # => NaN
r = -a..5 # => -Infinity..5
r.include? 3 # => true
A co z dużymi liczbami?
Nic. Programujesz w Rubim, nie masz się czym stresować. Nie wierzysz?
a = 2**128 # => 340282366920938463463374607431768211456
a.class # Bignum
a = a - 1 # => 340282366920938463463374607431768211455
Liczby zespolone
Biblioteka standardowa Rubiego daje nam bardzo wygodne wsparcie dla liczb zespolonych.
require 'complex' # => true
a = Complex(1, 3) # => Complex(1, 3)
b = Complex(-1.5, 5.17) # => Complex(-1.5, 5.17)
# Synonim:
a = 1 + 3.im # => Complex(1, 3)
2.im # => Complex(0, 2)
a.real # => 1
a.image # => 3
a.conj # => Complex(1, -3)
a.abs # => 3.16227766016838
# kwadrat modułu
a.abs2 # => 10
# argument w radianach
a.angle # => 1.24904577239825
a.polar # [3.16227766016838, 1.24904577239825]
a.to_s # => "1+3i"
a + b # => Complex(-0.5, 8.17)
a/b # => Complex(0.483455203613664, -0.333691064878239)
Complex::I**2 # => Complex(-1, 0)
Math::E**(Math::PI*Complex::I)
# => Complex(-1.0, 1.22460635382238e-016)
# Zwrócmy uwagę na pewną niedokładność części urojonej
Moduł Math
Moduł ten znamy z wielu innych języków programowania. Pozwala liczyć pierwiastki arytmetyczne, logarytmy naturalne, dziesiętne, eksponenty, funkcje trygonometryczne, arcusy, funkcje hiperboliczne i arcusy tychże. Oprócz tego zawiera parę ciekawych metod (ri!), w tym hypot
:
include Math # => Object
hypot(3, 4) # => 5.0
Metoda zwraca oczywiście długość przeciwprostokątnej trójkąta o danych dwóch przyprostokątnych.
Rozwiązujemy układ równań liniowych
Rozwiążmy następujący układ równań:
a + 2b = 3
4a + 5b = 6
W Rubim mamy do tego gotowe metody:
require 'mathn' # => true
m = Matrix[[1, 2], [4, 5]] # => Matrix[[1, 2], [4, 5]]
y = Matrix.column_vector([3, 6]) # => Matrix[[3], [6]]
(m.inv*y) # => Matrix[[-1], [2]]
I mamy rozwiązanie. a = -1, b = 2. Prawda, że proste? :-)
Przyznam, że w niedalekiej przyszłości planuję napisać nieco więcej o matematycznych możliwościach Rubiego. Bo są one imponujące - odczułem to na własnej, realizując w nim projekt do obliczeń numerycznych w elektronice.