Pomimo przeczytania „ Zrozumienia symboli rubinowych ”, nadal jestem zdezorientowany reprezentacją danych w pamięci, jeśli chodzi o używanie symboli. Jeśli symbol, dwa z nich zawarte w różnych obiektach, istnieją w tym samym miejscu pamięci, to jak to się dzieje, że zawierają różne wartości? Spodziewałbym się, że ta sama lokalizacja pamięci będzie zawierać tę samą wartość.
To cytat z linku:
W przeciwieństwie do łańcuchów, symbole o tej samej nazwie są inicjowane i istnieją w pamięci tylko raz podczas sesji programu ruby
Nie rozumiem, jak udaje mu się rozróżniać wartości zawarte w tej samej lokalizacji pamięci.
Rozważmy ten przykład:
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }
patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094
patient1
i patient2
oba są hashami, w porządku. :ruby
jednak jest symbolem. Gdybyśmy mieli wyprowadzić następujące dane:
patient1.each_key {|key| puts key.to_s}
Co wtedy zostanie wydrukowane? "red"
lub "programming"
?
Zapominając na chwilę o hashach, myślę, że symbol jest wskaźnikiem do wartości. Mam następujące pytania:
- Czy mogę przypisać wartość do symbolu?
- Czy symbol jest tylko wskaźnikiem do zmiennej zawierającej wartość?
- Jeśli symbole są globalne, czy oznacza to, że symbol zawsze wskazuje na jedną rzecz?
puts patient1[:ruby]
, wydrukuje "czerwony", jeśli powieszputs patient2[:ruby]
, wypisze "programowanie".Odpowiedzi:
Rozważ to:
x = :sym y = :sym (x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true x = "string" y = "string" (x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false
Tak więc, jakkolwiek tworzysz obiekt symboliczny, o ile jego zawartość jest taka sama, będzie odnosił się do tego samego obiektu w pamięci. Nie stanowi to problemu, ponieważ symbol jest niezmiennym przedmiotem . Ciągi znaków są zmienne.
(W odpowiedzi na komentarz poniżej)
W oryginalnym artykule wartość nie jest przechowywana w symbolu, ale w hashu. Rozważ to:
hash1 = { "string" => "value"} hash2 = { "string" => "value"}
Tworzy to sześć obiektów w pamięci - cztery obiekty typu string i dwa obiekty hash.
hash1 = { :symbol => "value"} hash2 = { :symbol => "value"}
To tworzy tylko pięć obiektów w pamięci - jeden symbol, dwa ciągi znaków i dwa obiekty z krzyżykiem.
źródło
Hash
(utworzony przez {... => ...} w twoim kodzie) przechowuje pary klucz / wartość, a nieSymbol
same s. WSymbol
s (np:symbol
lub:sym
lub:ruby
) są klucze w parach. Tylko jako częśćHash
„wskazują” na cokolwiek.Byłem w stanie naśladować symbole, kiedy o tym myślałem. Łańcuch Ruby to obiekt, który ma kilka metod i właściwości. Ludzie lubią używać łańcuchów jako kluczy, a kiedy łańcuch jest używany jako klucz, wszystkie te dodatkowe metody nie są używane. Dlatego stworzyli symbole, które są obiektami łańcuchowymi z usuniętymi wszystkimi funkcjami, z wyjątkiem tych, które są potrzebne, aby był to dobry klucz.
Pomyśl o symbolach jako o stałych ciągach.
źródło
Symbol
:ruby
nie zawiera"red"
ani"programming"
. Symbol:ruby
jest tylko symbolem:ruby
. To twoje skrótypatient1
ipatient2
każdy zawiera te wartości, w każdym przypadku wskazywane przez ten sam klucz.Pomyśl o tym w ten sposób: jeśli wejdziesz do salonu w bożonarodzeniowy poranek i zobaczysz dwa pudełka z etykietą z napisem „Kezzer”. Jeden ma w sobie skarpetki, a drugi węgiel. Nie będziesz się mylić i nie zapytasz, jak „Kezzer” może zawierać zarówno skarpety, jak i węgiel, mimo że to ta sama nazwa. Ponieważ nazwa nie zawiera (gównianych) prezentów. Po prostu na nich wskazuje. Podobnie
:ruby
nie zawiera wartości z twojego skrótu, po prostu je wskazuje.źródło
mystring = :steveT
że symbol niczego nie wskazuje. Klucz w skrócie ma skojarzoną wartość, a kluczem może być symbol. Ale symbol nie musi znajdować się w haszu.Możesz przypuszczać, że deklaracja, którą złożyłeś, definiuje wartość symbolu jako coś innego niż to, czym jest. W rzeczywistości Symbol jest po prostu „zinternalizowaną” wartością ciągu, która pozostaje stała. To dlatego, że są one przechowywane przy użyciu prostego identyfikatora liczby całkowitej, są często używane, ponieważ jest to bardziej wydajne niż zarządzanie dużą liczbą ciągów o zmiennej długości.
Weźmy przykład:
patient1 = { :ruby => "red" }
Należy to rozumieć jako: „zadeklaruj zmienną pacjenta1 i zdefiniuj ją jako hash, aw tym magazynie wartość 'red' pod kluczem (symbol 'ruby')"
Innym sposobem zapisania tego jest:
patient1 = Hash.new patient1[:ruby] = 'red' puts patient1[:ruby] # 'red'
Kiedy wykonujesz zadanie, nie jest zaskakujące, że wynik, który otrzymujesz, jest identyczny z tym, z którym go przypisałeś.
Koncepcja Symbol może być nieco myląca, ponieważ nie jest cechą większości innych języków.
Każdy obiekt String jest inny, nawet jeśli wartości są identyczne:
[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v| puts v.inspect + ' ' + v.object_id.to_s end # "foo" 2148099960 # "foo" 2148099940 # "foo" 2148099920 # "bar" 2148099900 # "bar" 2148099880 # "bar" 2148099860
Każdy symbol o tej samej wartości odnosi się do tego samego obiektu:
[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v| puts v.inspect + ' ' + v.object_id.to_s end # :foo 228508 # :foo 228508 # :foo 228508 # :bar 228668 # :bar 228668 # :bar 228668
Konwersja ciągów znaków na symbole odwzorowuje identyczne wartości na ten sam unikalny symbol:
[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v| v = v.to_sym puts v.inspect + ' ' + v.object_id.to_s end # :foo 228508 # :foo 228508 # :foo 228508 # :bar 228668 # :bar 228668 # :bar 228668
Podobnie, konwersja z Symbol na String tworzy za każdym razem odrębny ciąg:
[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v| v = v.to_s puts v.inspect + ' ' + v.object_id.to_s end # "foo" 2148097820 # "foo" 2148097700 # "foo" 2148097580 # "bar" 2148097460 # "bar" 2148097340 # "bar" 2148097220
Możesz myśleć o wartościach Symbol jako o wartościach rysowanych z wewnętrznej tabeli Hash i możesz zobaczyć wszystkie wartości, które zostały zakodowane do Symbols za pomocą prostego wywołania metody:
Symbol.all_values # => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...
W miarę definiowania nowych symboli za pomocą notacji dwukropków lub przy użyciu .to_sym ta tabela będzie się powiększać.
źródło
Symbole nie są wskazówkami. Nie zawierają wartości. Symbole po prostu są .
:ruby
jest symbolem:ruby
i to wszystko. Nie zawiera wartość, to nie robić , istnieje po prostu jako symbol:ruby
. Symbol:ruby
jest wartością, tak jak liczba 1. Nie wskazuje innej wartości bardziej niż liczba 1.źródło
Ani też, nie wyświetli "ruby".
Mylisz symbole i skróty. Nie są spokrewnione, ale są przydatne razem. Symbol, o którym mowa, to
:ruby
; nie ma to nic wspólnego z wartościami w hashu, a jej wewnętrzna reprezentacja w postaci liczb całkowitych będzie zawsze taka sama, a jej „wartość” (po przekonwertowaniu na łańcuch) zawsze będzie miała wartość „ruby”.źródło
W skrócie
Symbole rozwiązują problem tworzenia czytelnych dla człowieka, niezmiennych reprezentacji, które mają również tę zaletę, że są prostsze w wyszukiwaniu przez środowisko wykonawcze niż ciągi. Pomyśl o tym jak o nazwie lub etykiecie, których można użyć ponownie.
Dlaczego: czerwony jest lepszy niż „czerwony”
W dynamicznych językach obiektowych tworzysz złożone, zagnieżdżone struktury danych z czytelnymi odniesieniami. Hash jest częstym przypadkiem użycia gdzie odwzorować wartości unikatowych kluczy - unikalny, przynajmniej do każdej instancji. Nie możesz mieć więcej niż jednego „czerwonego” klucza na hash.
Jednak bardziej wydajnym procesorem byłoby użycie indeksu liczbowego zamiast kluczy łańcuchowych. Dlatego symbole zostały wprowadzone jako kompromis między szybkością a czytelnością. Symbole są znacznie łatwiejsze do rozpoznania niż ich odpowiednik. Będąc czytelnym dla człowieka i łatwym do rozpoznawania przez środowisko wykonawcze, symbole są idealnym dodatkiem do dynamicznego języka.
Korzyści
Ponieważ symbole są niezmienne, mogą być współużytkowane w czasie wykonywania. Jeśli dwie instancje skrótu mają wspólne leksykograficzne lub semantyczne zapotrzebowanie na czerwony element, symbol: red zużywałby mniej więcej połowę pamięci, której ciąg „red” wymagałby dla dwóch haszów.
Ponieważ: czerwony zawsze powraca do tej samej lokalizacji w pamięci, może być ponownie użyty w stu wystąpieniach skrótu prawie bez zwiększania pamięci, podczas gdy użycie „czerwonego” spowoduje dodanie kosztu pamięci, ponieważ każda instancja skrótu musiałaby przechowywać zmienny ciąg znaków kreacja.
Nie jestem pewien, jak Ruby faktycznie implementuje symbole / ciągi znaków, ale wyraźnie symbol oferuje mniejszy narzut implementacji w środowisku wykonawczym, ponieważ jest stałą reprezentacją. Symbole plus wymagają o jeden znak mniej niż ciąg cytowany w cudzysłowie, a mniej pisania to wieczna pogoń prawdziwych Rubyistów.
Podsumowanie
Z symbolem takim jak: red uzyskujesz czytelność reprezentacji ciągu z mniejszym narzutem ze względu na koszt operacji porównywania ciągów i konieczność przechowywania każdej instancji ciągu w pamięci.
źródło
Polecam przeczytanie artykułu w Wikipedii o tablicach z haszowaniem - myślę, że pomoże ci to zrozumieć, co
{:ruby => "red"}
tak naprawdę oznacza.Kolejne ćwiczenie, które może pomóc ci zrozumieć sytuację: rozważ
{1 => "red"}
. Semantycznie nie oznacza to „ustaw wartość1
na"red"
”, co jest niemożliwe w Rubim. Oznacza raczej „utwórz obiekt Hash i zapisz wartość"red"
klucza1
.źródło
Ani też, oczywiście. Dane wyjściowe będą
ruby
. A tak przy okazji, mogłeś się dowiedzieć w krótszym czasie, niż zajęło ci wpisanie pytania, po prostu wpisując je w IRB.Dlaczego miałoby to być
red
lubprogramming
? Symbole zawsze oceniają się same. Wartość symbolu:ruby
to:ruby
sam symbol , a ciąg znaków reprezentujący symbol:ruby
to wartość ciągu"ruby"
.[BTW: w
puts
każdym razie zawsze konwertuje swoje argumenty na łańcuchy. Nie ma potrzeby do tego dzwonićto_s
.]źródło
Jestem nowy w Ruby, ale myślę (mam nadzieję?), Że jest to prosty sposób na to ...
Symbol nie jest zmienną ani stałą. Nie zastępuje wartości ani nie wskazuje na nią. Symbol JEST wartością.
Wszystko to jest łańcuchem bez narzutu obiektu. Tekst i tylko tekst.
Więc to:
"hellobuddy"
Jest taki sam jak ten:
:hellobuddy
Z wyjątkiem tego, że nie możesz zrobić, na przykład: hellobuddy.upcase. Jest to wartość ciągu i TYLKO wartość ciągu.
Podobnie to:
greeting =>"hellobuddy"
Jest taki sam jak ten:
greeting => :hellobuddy
Ale znowu bez narzutu obiektu ciągu.
źródło
Jednym z łatwych sposobów, by to owinąć, jest myślenie: „co by było, gdybym użył sznurka zamiast symbolu?
patient1 = { "ruby" => "red" } patient2 = { "ruby" => "programming" }
To wcale nie jest mylące, prawda? Używasz „ruby” jako klucza w skrócie .
"ruby"
jest literałem łańcuchowym, więc to jest wartość. Adres pamięci lub wskaźnik nie są dostępne. Za każdym razem, gdy wywołujesz"ruby"
, tworzysz nowe jego wystąpienie, to znaczy tworzysz nową komórkę pamięci zawierającą tę samą wartość -"ruby"
.Hash następnie brzmi „jaka jest moja wartość klucza? Och, to jest
"ruby"
. Następnie mapuje tę wartość na„ czerwony ”lub„ programowanie ”. Innymi słowy,:ruby
nie odwołuje się do"red"
lub"programming"
. Hash jest mapowany:ruby
na"red"
lub"programming"
.Porównaj to z sytuacją, gdy używamy symboli
patient1 = { :ruby => "red" } patient2 = { :ruby => "programming" }
Wartość
:ruby
jest również"ruby"
skuteczna.Czemu? Ponieważ symbole są zasadniczo stałymi łańcuchami . Stałe nie mają wielu instancji. To ten sam adres pamięci. A adres pamięci ma określoną wartość po wyłuskowaniu. W przypadku symboli nazwa wskaźnika jest symbolem, a wyłuskaną wartością jest ciąg, który pasuje do nazwy symbolu, w tym przypadku
"ruby"
.W haszu nie używasz symbolu, wskaźnika, ale odroczoną wartość. Nie używasz
:ruby
, ale"ruby"
. Następnie hash wyszukuje klucz"ruby"
, wartość to"red"
lub"programming"
, w zależności od tego, jak zdefiniowałeś skrót.Koncepcja zmiany paradygmatu i zabierania do domu polega na tym, że wartość symbolu jest pojęciem całkowicie odrębnym od wartości odwzorowanej przez hash, biorąc pod uwagę klucz tego skrótu.
źródło