Przedziały, liczby i wstęp do matematyki w Rubim

Posted by Jacek Galanciak on

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.