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.

Komentarze: Skocz na dół, na górę

  1. Szkoda, że IRBa nie można na maturę ze sobą zabrać... :/ Jutro z matematyki właśnie.

    Pozdrawiam, stały czytelnik kursu.

  2. Żeby się nie obyło bez krytyki:
    1. hypot = lambda a, b: abs (a + i*b) ;-)
    2. Ciekawe, że jest to w stdlib w Ruby (nie znam chyba drugiego takiego języka ;), choć z drugiej strony odwracanie macierzy to dość kiepska metoda na rozwiązywanie równań macierzowych. ;-)
    Btw. Jest dla Ruby coś porównywalnego z numpy?
    3. Rozróżnienie między przedziałem ‘..’ i ‘...’ jest powalającym pomysłem. Jedynym gorszym mogłoby chyba być dodanie jeszcze przedziału konstruowanego za pomocą ‘....’. ;-)

    Ala ogólnie miły wpis. :)

    Pozdrawiam,
    miłośnik Pythona i Scheme ;)

  3. Ok, troche pomarudziłem, potem przeczytałem notkę o autorze i wtem nasunęło mi się pytanie: jaki wydział, kierunek i rok studiów. ;-)

  4. (Komentarz zmodyfikowany 14.05.2007 o 20:38)

    Wpis bardzo fajny, jak zwykle :). Przydałby się jednak jeszcze jakiś przykładowy program, taki z życia wzięty :).

    3. Rozróżnienie między przedziałem ‘..’ i ‘...’ jest powalającym pomysłem. Jedynym gorszym mogłoby chyba być dodanie jeszcze przedziału konstruowanego za pomocą ‘....’. ;-)

    Hm, co w tym złego? Czepiający się Pythonista?:P

    Co do przedziałów (Range) dodam, że można bardzo łatwo tworzyć klasy, które się tak zachowują:

    
    class XLetters
        include Comparable
        attr_accessor :length
    
        def initialize(n)
            @length = n
        end
    
        def succ
            XLetters.new(length + 1)
        end
    
        def <=>(other)
            self.length <=> other.length
        end
    
        def inspect
            'x' * length
        end
    end
    
    range = XLetters.new(3)..XLetters.new(10)
    p range.to_a
    p range.include?(XLetters.new(5))
    #i inne (min, max etc)
    

    ps. czemu nie ma podglądu komentarzy?:)

  5. Te kropki faktycznie nie są specjalnie udane. Czytelność może być dość niska (o ile różnicę między „>=” a „>” widać w kodzie dość wyraźnie, o tyle „..” może niewiele się różnić od „...”). To kwestia fontów głównie, ale mimo wszystko…

    Poza tym to nieintuicyjne. Gdyby ktoś mnie zapytał czym się różni „..” od „...”, to do głowy by mi nie przyszło, że „...” jest dłuższe i dlatego opisuje przedziały niedomknięte, a „..” jest krótsze i opisuje krótsze przedziały domknięte :)

    Ale nie mam pojęcia jaki sposób byłby naprawdę lepszy.

  6. JPC:
    Biblioteka standardowa Rubiego posiada wiele ciekawych możliwości (dobre wsparcie dla macierzy, rozkłady LU [a-ha! da się bez odwracania macierzy], różniczkowanie numeryczne, jakobiany, algorytm Newtona-Raphsona itp.). Widziałem w sieci sporo bibliotek, które oferują bardziej wyrafinowane metody analizy numerycznej, przetwarzania sygnałów itp. Nie znam NumPy, ale z tego, co o nim czytałem, to nie wyprzedza tego, co może Ruby ze swoją artylerią. Temat obliczeń numerycznych mnie zainteresował, więc na pewno napiszę jeszcze co najmniej dwa artykuły o takim zastosowaniu Rubiego.

    A co do moich studiów – Politechnika Łódzka, Wydział Elektrotechniki, Elektroniki, Informatyki i Automatyki, kierunek: Elektronika i Telekomunikacja, sem 4 :).

    Radarek: Dodałem podgląd. Dzięki za motywację :).

    Hoppke: Może i takie kropki nie są zbyt intuicyjne, ale, jak słusznie zauważyłeś, ciężko o coś lepszego. Nawet to, co znamy z matematyki, np. [5, 10) jest czymś czysto umownym i nieczytelnym dla kogoś, kto w temacie nie siedzi.

  7. No popatrz, jaki świat mały. Ja to samo, ale na 6 semestrze. ;-)
    Moglibyśmy się na piwo umówić, ale to takie odrobine kłopotliwe, ponieważ piwa nie pijam. ;-)

    A’propos kropek: są nieczytelnie i już. Coś jak iteracja po stringach w Pythonie, niby fajne, ale w praktyce zbyt często daje bardzo dziwne błędy, które trudno wykryć, bo wydaje się, że tam w tym forze na pewno jest lista a nie nic innego (czyli ten string właśnie). Nie ma to związku z umownością, bo umowa matematyczna może jest arbitralna, ale przynajmniej trudno pomylić jeden nawias z drugim (chyba, że to kolokwium i student, niewiedząc, o który mu chodzi, pisze niewyraźnie ;).

  8. Ulepszyłem artykuł. Dodałem do przykładu z liczbami zespolonymi kod ze sprzężeniem, argumentem itp, poza tym rozbudowałem przykład z zapisem liczb. Czegoś mi po prostu brakowało ;).

    jpc: Moje namiary masz w sidebarze, więc… :>

Dodaj komentarz na temat

Zanim skomentujesz...

W komentarzach działają znaczniki Textile.
Zastrzegam sobie prawo do edycji Twojego komentarza tylko i wyłącznie w celach estetycznych (naprawienie źle wstawionego kodu, itp). Nie zmieniam ich treści, ortografii, interpunkcji. Jeśli odczuwasz potrzebę edycji swojego komentarza, skontaktuj się ze mną, a zdziałamy co trzeba.