Witaj na blogu prowadzonym przez Sebana. Spisuję tutaj swoje uwagi na różny temat. Przeważają tematy związane z Internetem, popieranymi przeze mnie rozwiązaniami dotyczącymi wykorzystania komputerów, oraz kilka innych. Przeczytasz tu również recenzje książek IT.
Równy, równiejszy, unikalny w Ruby
19 czerwca 2012 | Klucze:
it,
Ogólne,
programowanie,
rails,
ruby,
Techblog
6 komentarzy. trackback
W Ruby piszę już 5 lat. Dość czasu by pomyśleć, że sporo o tym języku już wiem. Jednak ostatnio sam zapędziłem się w kozi róg i musiałem trochę poszukać rozwiązania mojego problemu. Mianowicie miałem klasę powiedzmy OfferItem (prosta klasa bez ActiveRecord itp.), kilka instancji tej klasy w tablicy i chciałem wyciągnąć tylko unikalne, bez duplikatów. Problem wydaje się prosty - na tablicy wywołać metodę uniq. Jednak ja nie wiedziałem (tak na pewno) jaką metodę w klasie OfferItem napisać by uniq potrafił rozpoznać unikalne.
Rozwiązanie
Moim pierwszym podejściem było napisanie metody ==. Byłem niemal pewien, że to mi pomoże. Nie pomogło. Otóż == sprawdza tylko równość, która nie ma nic wspólnego z tym co sprawdza metoda uniq. Po chwili wyszukiwania odnalazłem odpowiedź. Metoda Array#uniq do wykrywania duplikatów używa metod Object#eql? i Object#hash. Nic innego tylko te dwie metody. Zatem nawet jeśli dwa obiekty klasy OfferItem zwracają true przy porównaniu za pomocą ==, to dopóki samemu nie zaimplementuje się OfferItem#eql i OfferItem#hash uniq nie ma szans zadziałać.
== porównuje wartości. Każdy wie, że 14 == 14.0 i nikt się temu nie dziwi, ponieważ wartość jest taka sama.
eql? porównuje wartości, przeważnie. W klasie Numeric dokonywana jest konwersja dlatego 14.eql? 14.0 daje false. W tym przykładzie niech klasa OfferItem ma taką sam implementację eql? co ==.
Object#hash zwraca wartość skrótu dla każdego obiektu. Metoda hash musi być tak zaimplementowana by dla dwóch obiektów o tym samym skrócie a_obj.eql? b_obj.
Całość implementacji:
Czytanki
- Warto zajrzeć jak porównania zaimplementowane są w gemie Virtus - Virtus::Equalizer::Methods. Przy okazji polecam zabawę i czytanie kodu tego gema.

