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