Skip to content
← Wszystkie wpisy
4 min czytania Dawid Skłodowski

Ruby 2.4: ujednolicony Integer i szybsze hashe

Ruby 2.4.0 przybywa w Boże Narodzenie z dawno spóźnionym porządkiem — Fixnum i Bignum scalone w jeden Integer — plus szybszy Hash, Enumerable#sum, Regexp#match? i binding.irb. Praktyczny przegląd.

Ruby 2.4.0 przybyło zgodnie z planem na Boże Narodzenie i jest wydaniem z satysfakcjonującą mieszanką dawno spóźnionego porządku i cichej pracy nad wydajnością. Nagłówkiem jest uproszczenie, które docenisz koncepcyjnie, nawet jeśli mało zmienia w twoim kodzie, a pod nim są realne przyspieszenia i garść naprawdę użytecznych nowych metod. Oto przegląd.

Fixnum i Bignum stają się Integer

Przez całą swoją historię Ruby miało dwie klasy liczb całkowitych: Fixnum dla małych liczb mieszczących się w słowie maszynowym i Bignum dla większych, z Ruby przezroczyście promującym między nimi. Rzadko zauważałeś — 2 + 2 i 10**100 po prostu działały — ale podział był przeciekającą abstrakcją, która czasem wychodziła w mylące sposoby (klasa wartości zmieniająca się, gdy rosła, sprawdzenia is_a?(Fixnum) psujące się na dużych liczbach). 2.4 ujednolica je w pojedynczą klasę Integer:

1.class           # => Integer   (było Fixnum)
(10**100).class   # => Integer   (było Bignum)

Zachowanie jest identyczne — Ruby wciąż używa wydajnej reprezentacji wewnętrznie dla małych liczb — ale model koncepcyjny jest teraz czysty: liczba całkowita to Integer, kropka. Fixnum i Bignum pozostają jako deprecjonowane aliasy, by stary kod się nie psuł, ale właściwym modelem myślowym i właściwą rzeczą do odwoływania się jest teraz po prostu Integer. To rodzaj porządku, który czyni język łatwiejszym do nauczania i ogarniania, usuwając rozróżnienie, które nigdy nie powinno być widoczne dla użytkowników.

Szybsze hashe

Hashe są wszędzie w Ruby — opcje, JSON, każdy argument nazwany, wnętrzności samego języka — więc uczynienie ich szybszymi to szeroka wygrana. Ruby 2.4 przerabia implementację Hash, by używała otwartego adresowania, bardziej przyjaznej dla cache struktury wewnętrznej poprawiającej wydajność wyszukiwania i wstawiania, zwłaszcza dla małych-do-średnich hashy dominujących w realnym kodzie. Nie zmieniasz nic, a twój mocno hashowy kod robi się szybszy, co na typowym żądaniu Rails — zbudowanym na górze hashy — jest darmową, szeroką poprawą dokładnie tego rodzaju, który czyni aktualizację wartą jej.

Enumerable#sum: nareszcie

Mały dodatek usuwający maleńkie codzienne tarcie. Sumowanie kolekcji oznaczało kiedyś inject(:+) albo reduce(0, :+) — w porządku, ale lekko zagadkowe i wolniejsze, niż musiało być. 2.4 dodaje porządne Enumerable#sum:

[1, 2, 3, 4].sum            # => 10
items.sum(&:price)          # => łączna cena
[1.0, 2.0].sum(100)         # => 103.0  (z wartością początkową)

Czyta się lepiej i jest zaimplementowane w C, więc jest szybsze niż inject, który zastępuje. Trywialna metoda, po którą mimo to będziesz sięgać nieustannie.

Regexp#match? : szybsze dopasowanie boolean

Gdy chcesz tylko wiedzieć, czy łańcuch pasuje do wzorca — nie przechwytując niczego — stare idiomy (=~, match) wykonują dodatkową pracę: ustawiają globalne zmienne dopasowania ($~, $1, …) i alokują obiekt MatchData. Regexp#match? po prostu zwraca boolean i nie robi nic z tego:

if /\A\d+\z/.match?(input)   # bez alokacji MatchData, bez ustawiania globali
  # ...
end

W gorącej pętli albo walidacji uruchamianej na każdym żądaniu pominięcie alokacji i księgowości zmiennych globalnych to mierzalna oszczędność. To właściwe narzędzie, ilekroć chcesz odpowiedzi tak/nie, a nie szczegółów dopasowania.

binding.irb: REPL gdziekolwiek

Urocze dodanie do debugowania. Możesz teraz wrzucić binding.irb do swojego kodu, by otworzyć pełną sesję IRB w tym punkcie, z dostępem do wszystkich zmiennych lokalnych — jak binding.pry z popularnego gemu, ale wbudowane w samo Ruby:

def calculate(order)
  binding.irb     # wykonanie zatrzymuje się tu; inspekcjonuj `order` interaktywnie
  # ...
end

Nie jest tak bogate w funkcje jak Pry, ale posiadanie prawdziwego REPL-w-breakpoincie w bibliotece standardowej, bez gemu do dodania, jest naprawdę użyteczne — zwłaszcza w środowiskach, gdzie wolałbyś nie dodawać zależności tylko po to, by pogrzebać w wartości.

Mniejsze dodatki

Dopełniając wydanie: Comparable#clamp ogranicza wartość do zakresu (n.clamp(0, 100)); Integer#digits zwraca cyfry liczby jako tablicę (123.digits → [3, 2, 1]); String#unpack1 zwraca tylko pierwszą rozpakowaną wartość zamiast tablicy; i są udoskonalenia Float#round oraz obsługi wielkości liter (w tym lepsze mapowanie wielkości Unicode). Niedramatyczna runda deprecjacji trwa jak zwykle.

Czy aktualizować?

Tak — to niskoryzykowne, szeroko korzystne wydanie. Ujednolicenie Integer jest głównie koncepcyjne i wstecznie zgodne (aliasy trzymają stary kod działającym), więc nic cię nie kosztuje i porządkuje model myślowy. Szybsze hashe to darmowa, szeroka wygrana wydajności dla dowolnego programu Ruby, podwójnie dla mocno hashowych aplikacji Rails. A nowe metody — sum, match?, clamp, binding.irb — to małe usprawnienia ergonomii, które przyjmiesz naturalnie, pisząc kod.

Wzorzec ostatnich wydań Ruby trzyma się stabilnie: posprzątaj brodawkę, która nigdy nie powinna być widoczna, uczyń wspólne struktury danych szybszymi za darmo i dodaj kilka ostrych metodek, które czynią codzienny kod lepiej czytelnym. 2.4 to nieefektowna, łatwa aktualizacja — najlepszy rodzaj.