Odpowiedzi:
TL; DR:
Używanie symboli nie tylko oszczędza czas podczas dokonywania porównań, ale także oszczędza pamięć, ponieważ są one przechowywane tylko raz.
Symbole rubinowe są niezmienne (nie można ich zmienić), co znacznie ułatwia wyszukiwanie czegoś
Krótka (ish) odpowiedź:
Używanie symboli nie tylko oszczędza czas podczas dokonywania porównań, ale także oszczędza pamięć, ponieważ są one przechowywane tylko raz.
Symbole w Rubim to po prostu „niezmienne ciągi znaków” … to znaczy, że nie można ich zmienić, co oznacza, że ten sam symbol, do którego odwołuje się wiele razy w kodzie źródłowym, jest zawsze przechowywany jako ta sama jednostka, np. Ma ten sam identyfikator obiektu .
Z drugiej strony łańcuchy są zmienne , można je zmienić w dowolnym momencie. Oznacza to, że Ruby musi przechowywać każdy napis, o którym wspominasz w kodzie źródłowym, w osobnej encji, np. Jeśli masz ciąg znaków „nazwa” wielokrotnie wspomniany w kodzie źródłowym, Ruby musi przechowywać je wszystkie w osobnych obiektach typu String, ponieważ może się później zmienić (taka jest natura łańcucha Ruby).
Jeśli używasz ciągu jako klucza Hash, Ruby musi oszacować ciąg i spojrzeć na jego zawartość (i obliczyć na nim funkcję skrótu) i porównać wynik z (zahaszowanymi) wartościami kluczy, które są już zapisane w Hash .
Jeśli używasz symbolu jako klucza Hash, jest domniemane, że jest niezmienny, więc Ruby może po prostu porównać (funkcję skrótu) object-id z (zaszyfrowanymi) identyfikatorami obiektów kluczy, które są już przechowywane w Hash. (o wiele szybciej)
Wada: Każdy symbol zajmuje miejsce w tabeli symboli interpretera Rubiego, które nigdy nie jest zwalniane. Symbole nigdy nie są zbierane jako śmieci. Tak więc przypadek narożny ma miejsce, gdy masz dużą liczbę symboli (np. Automatycznie generowanych). W takim przypadku powinieneś ocenić, jak wpływa to na rozmiar twojego interpretera Rubiego.
Uwagi:
Jeśli wykonujesz porównania ciągów, Ruby może porównywać symbole tylko po ich identyfikatorach obiektów, bez konieczności ich oceny. To znacznie szybsze niż porównywanie ciągów znaków, które należy ocenić.
Jeśli masz dostęp do skrótu, Ruby zawsze stosuje funkcję skrótu, aby obliczyć „klucz skrótu” z dowolnego klucza, którego używasz. Możesz sobie wyobrazić coś w rodzaju skrótu MD5. A potem Ruby porównuje te „zaszyfrowane klucze” ze sobą.
Długa odpowiedź:
Powodem jest wydajność, z wieloma korzyściami w ciągu:
O(n)
ciągów znaków i stałych symboli.Co więcej, Ruby 1.9 wprowadził uproszczoną składnię tylko dla hasha z kluczami symboli (np. h.merge(foo: 42, bar: 6)
), A Ruby 2.0 ma argumenty słów kluczowych, które działają tylko dla kluczy z symbolami.
Uwagi :
1) Możesz być zaskoczony, gdy dowiesz się, że Ruby traktuje String
klucze inaczej niż jakikolwiek inny typ. W rzeczy samej:
s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash # must be called whenever a key changes!
h[s] # => nil, not "bar"
h.keys
h.keys.first.upcase! # => TypeError: can't modify frozen string
Tylko dla kluczy łańcuchowych Ruby użyje zamrożonej kopii zamiast samego obiektu.
2) Litery „b”, „a” i „r” są zapisywane tylko raz dla wszystkich wystąpień :bar
w programie. Przed Rubim 2.2 ciągłe tworzenie nowych Symbols
, których nigdy nie używano ponownie, było złym pomysłem , ponieważ pozostały one na zawsze w globalnej tabeli wyszukiwania symboli. Ruby 2.2 zbierze je jako śmieci, więc nie martw się.
3) W rzeczywistości obliczenie skrótu dla symbolu nie zajęło czasu w Ruby 1.8.x, ponieważ identyfikator obiektu był używany bezpośrednio:
:bar.object_id == :bar.hash # => true in Ruby 1.8.7
W Rubim 1.9.x sytuacja uległa zmianie, ponieważ skróty zmieniają się z jednej sesji na drugą (łącznie z tymi w Symbols
):
:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Re: jaka jest przewaga nad używaniem sznurka?
(Bardzo) nieco szybsze wyszukiwanie wartości, ponieważ haszowanie symbolu jest równoważne haszowaniu liczby całkowitej w porównaniu z haszowaniem ciągu.
Wada: zużywa miejsce w tablicy symboli programu, które nigdy nie jest zwalniane.
Byłbym bardzo zainteresowany kontynuacją dotyczącą zamrożonych ciągów znaków wprowadzonych w Ruby 2.x.
Kiedy masz do czynienia z wieloma ciągami pochodzącymi z danych wejściowych (myślę o parametrach HTTP lub ładunku, na przykład przez Rack), łatwiej jest używać ciągów wszędzie.
Kiedy masz do czynienia z dziesiątkami z nich, ale one nigdy się nie zmieniają (jeśli są to „słownictwo” Twojej firmy), lubię myśleć, że ich zamrożenie może coś zmienić. Nie zrobiłem jeszcze żadnego testu porównawczego, ale myślę, że byłoby to blisko wydajności symboli.