Ruby'de semboller nasıl anlaşılır


85

" Ruby Sembollerini Anlamak " ı okumamıza rağmen , sembollerin kullanılması söz konusu olduğunda hafızadaki verilerin temsili hala kafam karışık. İkisi farklı nesnelerde bulunan bir sembol aynı hafıza konumunda bulunuyorsa, o zaman nasıl farklı değerler içerirler ? Aynı bellek konumunun aynı değeri içermesini beklerdim.

Bu bağlantıdan bir alıntı:

Dizelerden farklı olarak, aynı isimli semboller başlatılır ve Ruby'nin oturumu sırasında yalnızca bir kez bellekte bulunur.

Aynı hafıza konumunda bulunan değerleri farklılaştırmayı nasıl başardığını anlamıyorum.

Şu örneği düşünün:

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

patient1ve patient2ikisi de karmadır, sorun değil. :rubyancak bir semboldür. Aşağıdakileri çıkarırsak:

patient1.each_key {|key| puts key.to_s}

O zaman çıktı ne olacak? "red"veya "programming"?

Karmaları bir saniyeliğine unutarak, bir sembolün bir değerin göstericisi olduğunu düşünüyorum . Sahip olduğum sorular:

  • Bir sembole değer atayabilir miyim?
  • Bir sembol, içinde değer bulunan bir değişkeni gösteren bir gösterici midir?
  • Semboller küreselse, bu bir sembolün her zaman bir şeyi işaret ettiği anlamına mı gelir?

1
Bir Sembol yazdırdığınız için ": ruby" çıktısı verecektir. Eğer derseniz puts patient1[:ruby]"kırmızı" yazacak puts patient2[:ruby], derseniz "programlama" yazacaktır.
ROSS

1
Bir sembol, bir değere işaretçi DEĞİLDİR. Dahili olarak bir sembol sadece bir tam sayıdır.
akuhn

Yanıtlar:


62

Bunu düşün:

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

Dolayısıyla, bir sembol nesnesi oluştursanız da, içeriği aynı olduğu sürece bellekte aynı nesneye başvuracaktır. Bu bir problem değildir çünkü bir sembol değişmez bir nesnedir . Dizeler değiştirilebilir.


(Aşağıdaki yoruma yanıt olarak)

Orijinal makalede, değer bir sembolde saklanmıyor, bir karma olarak saklanıyor. Bunu düşün:

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}

Bu, bellekte altı nesne oluşturur - dört dizi nesnesi ve iki karma nesne.

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}

Bu, bellekte yalnızca beş nesne oluşturur - bir sembol, iki dizi ve iki karma nesne.


Bununla birlikte bağlantıdaki örnek, farklı değerler içeren sembolleri gösterir , ancak sembol aynı ada ve aynı hafıza konumuna sahiptir. Çıktı olduklarında, farklı değerlere sahipler , anlamadığım kısım bu. Kesinlikle aynı değeri içermeli?
Kezzer

1
Kafamın hâlâ nasıl karıştığını anlatmak için bir düzenleme yaptım. Beynim hesaplayamıyor;)
Kezzer

48
Semboller değer içermez , değerdir. Hash değerleri içerir.
Mladen Jablanović

6
Bu oluyor Hashdepolar anahtar / değer çiftleri, o değil ({... => ...} kodunuzda tarafından oluşturulan) Symbolkendilerini bu. SymbolS (örn :symbolveya :symveya :ruby) çiftler halinde anahtarlarıdır. Sadece bir parçası olarak herhangi bir Hashşeyi "işaret ediyorlar".
James A. Rosen

1
Sembol, değerde değil, karmada anahtar olarak kullanılıyor, bu yüzden farklı olabilirler, key1 = 'ruby' ve hash1 = {key1 => 'value' ...} hash2 = {demeye benzer. anahtar1 => 'değer2' ...}.
Joshua Olson

53

Böyle düşündüğümde semboller sallayabildim. Bir Ruby dizesi, birçok yöntem ve özelliğe sahip bir nesnedir. İnsanlar anahtarlar için dizeleri kullanmayı severler ve dizge bir anahtar için kullanıldığında, tüm bu ekstra yöntemler kullanılmaz. Bu yüzden, iyi bir anahtar olması için gerekli olan dışında, tüm işlevleri kaldırılmış dizge nesneleri olan semboller yaptılar.

Sembolleri sabit dizeler olarak düşünün.


3
Mesajları okurken, bu muhtemelen benim için en mantıklı olanı. : yakut sadece hafızada bir yerde saklanır, eğer bir yerde "yakut", sonra tekrar bir yerde "yakut" kullanırsam, bu sadece bir kopya. Dolayısıyla, semboller kullanmak, ortak verilerin tekrarlanmasını azaltmanın bir yoludur. Dediğiniz gibi, sabit dizeler. Sanırım bu sembolü tekrar kullanmak için bulacak temel bir mekanizma var?
Kezzer

1
@Kezzer Bu cevap gerçekten iyi ve bana doğru geliyor, ancak yorumunuz farklı bir şey söylüyor ve yanlış veya yanıltıcı, yorumunuz verilerin dizelerle kopyalanmasından bahsediyor ve bu sembollerin nedeni, bu yanlış veya yanıltıcı. sembol birden çok kez daha fazla bellek alanı kullanmaz, ancak buna birçok dilde dizeler için de sahip olabilirsiniz, örneğin bazı programlama dillerinde "abc" yazarsanız ve başka bir yerde "abc" yazarsanız derleyici aynı değer dizesini görür ve saklar aynı yerde onu aynı nesne yapmak, buna string interning denir ve c # bunu yapar.
barlop

Yani temelde bir dizginin inanılmaz derecede hafif bir versiyonu mu?
stevec

34

Sembol veya :rubyiçermez . Sembol sadece semboldür . Karmalarınızdır ve her biri bu değerleri içerir, her durumda aynı anahtarla gösterilir."red""programming":ruby:rubypatient1patient2

Şöyle düşünün: Noel sabahı salona girip üzerinde "Kezzer" yazan iki kutu etiketi olan iki kutu görürseniz. Onda çorap var, diğerinde kömür var. Kafanız karışmayacak ve "Kezzer" aynı isim olmasına rağmen nasıl hem çorap hem de kömür içerebileceğini sormayacaksınız. Çünkü isim (berbat) hediyeleri içermiyor. Sadece onlara işaret ediyor. Benzer şekilde, :rubyhash'inizdeki değerleri içermez, sadece onlara işaret eder.


2
Bu cevap tamamen mantıklı.
Vass

Bu, toplam karma ve sembol karışımı gibi geliyor. Bir sembol bir değere işaret etmez, eğer bir hash içindeyken olduğunu söylemek isterseniz, bu tartışılabilir olabilir, ancak bir sembolün bir hash içinde olması gerekmez. mystring = :steveT Sembolün hiçbir şeye işaret etmediğini söyleyebilirsin . Karma içindeki bir Anahtarın ilişkili bir değeri vardır ve anahtar bir sembol olabilir. Ancak bir sembolün karma olması gerekmez.
barlop

27

Yaptığınız bildirimin, bir Sembolün değerini, olduğundan farklı bir şey olarak tanımladığını varsayıyor olabilirsiniz. Aslında, bir Sembol, sabit kalan "içselleştirilmiş" bir Dize değeridir. Basit bir tamsayı tanımlayıcısı kullanılarak depolandıkları için, çok sayıda değişken uzunluklu dizgiyi yönetmekten daha verimli olduğu için sıklıkla kullanılmalarıdır.

Örneğinizin durumunu ele alalım:

patient1 = { :ruby => "red" }

Bu şu şekilde okunmalıdır: "hasta1 değişkenini bildirin ve bunu Hash olarak tanımlayın ve bu anahtarın altında" kırmızı "değerini saklayın (" yakut "simgesi)"

Bunu yazmanın başka bir yolu:

patient1 = Hash.new
patient1[:ruby] = 'red'

puts patient1[:ruby]
# 'red'

Bir ödev verirken, geri aldığınız sonucun, ilk başta atadığınızla aynı olması şaşırtıcı değildir.

Sembol kavramı, diğer dillerin çoğunun bir özelliği olmadığı için biraz kafa karıştırıcı olabilir.

Değerler aynı olsa bile her String nesnesi farklıdır:

[ "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

Aynı değere sahip her Sembol aynı nesneyi ifade eder:

[ :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

Dizeleri sembollere dönüştürmek, aynı değerleri aynı benzersiz Sembol ile eşler:

[ "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

Benzer şekilde, Sembol'den Dize'ye dönüştürmek her seferinde ayrı bir dize oluşturur:

[ :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

Sembol değerlerinin dahili bir Karma tablosundan çizildiğini düşünebilir ve basit bir yöntem çağrısı kullanarak Sembollere kodlanmış tüm değerleri görebilirsiniz:

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=, ...

Yeni sembolleri ya iki nokta üst üste gösterimi ile ya da .to_sym kullanarak tanımladığınızda, bu tablo büyüyecektir.


18

Semboller işaretçi değildir. Değerler içermezler. Semboller basitçe vardır . :rubysemboldür :rubyve hepsi bu kadar. O değil, bir değer içermiyor yapmak sadece sembol olarak var, bir şey :ruby. Sembol :ruby, 1 sayısı gibi bir değerdir. 1 rakamından daha fazla başka bir değere işaret etmez.


13
patient1.each_key {|key| puts key.to_s}

O zaman çıktı ne olacak? "kırmızı" mı yoksa "programlama" mı?

İkisi de "yakut" çıkacak.

Kafa karıştırıcı semboller ve karmalar. İlişkili değiller ama birlikte kullanışlıdırlar. Söz konusu sembol :ruby; karmadaki değerlerle ilgisi yoktur ve iç tamsayı temsili her zaman aynı olacaktır ve "değeri" (bir dizeye dönüştürüldüğünde) her zaman "yakut" olacaktır.


11

Kısacası

Semboller, çalışma zamanı için dizelerden daha basit arama avantajına sahip, insan tarafından okunabilir, değişmez temsiller yaratma sorununu çözer. Yeniden kullanılabilecek bir isim veya etiket gibi düşünün.

Neden: kırmızı "kırmızı" dan daha iyidir

Dinamik nesne yönelimli dillerde, okunabilir referanslara sahip karmaşık, iç içe geçmiş veri yapıları oluşturursunuz. Karma bir yaygın kullanılma şeklidir en azından her örneğine, eşsiz - benzersiz tuşlara değerleri map. Hash başına birden fazla "kırmızı" anahtara sahip olamazsınız.

Bununla birlikte, dize anahtarları yerine sayısal bir dizin kullanmak işlemci açısından daha verimli olacaktır. Böylece semboller hız ve okunabilirlik arasında bir uzlaşma olarak tanıtıldı. Semboller, eşdeğer dizeden çok daha kolay çözülür. İnsan tarafından okunabilir olması ve çalışma zamanı için sembolleri çözmesi kolay olması, dinamik bir dile ideal bir ektir.

Faydaları

Semboller değişmez olduklarından, çalışma süresi boyunca paylaşılabilirler. İki karma örneğinin kırmızı bir öğeye yönelik ortak bir sözlükbilimsel veya anlamsal gereksinimi varsa, simge: kırmızı, "kırmızı" dizesinin iki karma için gereken belleğin kabaca yarısını kullanır.

Red her zaman bellekte aynı konuma geri döndüğünden, bellekte neredeyse hiç artış olmaksızın yüz karma örneğinde yeniden kullanılabilirken, "kırmızı" kullanımı, her bir karma örneğinin değiştirilebilir dizeyi depolaması gerekeceğinden bellek maliyeti ekleyecektir oluşturma.

Ruby'nin sembolleri / dizgeyi nasıl uyguladığından emin değilim, ancak açıkça bir sembol, sabit bir temsil olduğu için çalışma zamanında daha az uygulama yükü sunar. Artı semboller, tırnak içine alınmış bir dizeden bir daha az karakter gerektirir ve daha az yazım, gerçek Rubyistlerin ebedi arayışıdır.

Özet

Kırmızı gibi bir sembolle, dize karşılaştırma işlemlerinin maliyeti ve her dize örneğini bellekte saklama ihtiyacı nedeniyle daha az ek yük ile dize temsilinin okunabilirliğini elde edersiniz.


4

Karma tablolardaki Wikipedia makalesini okumanızı tavsiye ederim - bence bu, {:ruby => "red"}gerçekte ne anlama geldiğini anlamanıza yardımcı olacaktır .

Durumu anlamanıza yardımcı olabilecek başka bir alıştırma: düşünün {1 => "red"}. Semantik, demek değildir "değerini ayarlamak 1için "red"Ruby imkansızdır." Bunun yerine, "bir Hash nesnesi oluşturun "red"ve anahtarın değerini saklayın 1.


3
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

patient1ve patient2ikisi de karmadır, sorun değil. :rubyancak bir semboldür. Aşağıdakileri çıkarırsak:

patient1.each_key {|key| puts key.to_s}

O zaman çıktı ne olacak? "kırmızı" mı yoksa "programlama" mı?

Elbette hiçbiri. Çıktı olacak ruby. Hangisi, BTW, soruyu yazmak için harcadığınız zamandan daha kısa sürede, bunun yerine sadece IRB'ye yazarak öğrenebilirdiniz.

Neden olur Olması redveya programming? Semboller her zaman kendilerini değerlendirir. Sembolün :rubydeğeri, sembolün :rubykendisidir ve sembolün dize temsili :ruby, dize değeridir "ruby".

[BTW: putsyine de argümanlarını her zaman dizelere dönüştürür. to_sOnu aramanıza gerek yok .]


Şu anki makinede IRB yok, ben de onu yükleyemem, bu yüzden bunun için özür dilerim.
Kezzer

2
@Kezzer: Merak etme, sadece merak ettim. Bazen kendini o kadar derine gömüyorsun ki, artık en basit şeyleri bile göremiyorsun. Temel olarak sorunuzu IRB'ye yapıştırdığımda merak ettim: "Bunu neden kendisi yapmadı?" Ve endişelenme, cevap "sadece çalıştır!" Olduğunda "bu ne yazdırıyor" diye soran ilk değilsin (son da değilsin) BTW: işte anında IRB'niz , her yerde, her zaman, kurulum gerektirmez: TryRuby.Org veya Ruby-Versions.Net , şimdiye kadar piyasaya sürülen MRI + YARV + JRuby + Rubinius + REE'nin tüm sürümlerine SSH erişimi sağlar.
Jörg W Mittag

Teşekkürler, şimdi bununla uğraşıyorum. Yine de biraz kafam karıştı, bu yüzden tekrar üzerinden geçiyorum.
Kezzer

1

Ruby'de yeniyim, ama sanırım (umarım?) Bu, ona bakmanın basit bir yolu ...

Bir sembol, değişken veya sabit değildir. Bir değerin yerine geçmez veya bir değere işaret etmez. Bir sembol bir değerdir.

Hepsi bu, nesne ek yükü olmayan bir dizedir. Metin ve yalnızca metin.

Yani, bu:

"hellobuddy"

Şununla aynı:

:hellobuddy

Yapamayacağınız dışında, örneğin: hellobuddy.upcase. Bu dize değeridir ve YALNIZCA dize değeridir.

Aynı şekilde, bu:

greeting =>"hellobuddy"

Şununla aynı:

greeting => :hellobuddy

Ancak yine, dize nesnesi yükü olmadan.


-1

Kafanızı bu konuya almanın kolay bir yolu, "Ya bir sembol yerine bir ip kullansaydım?

patient1 = { "ruby" => "red" }
patient2 = { "ruby" => "programming" }

Hiç kafa karıştırıcı değil, değil mi? Bir anahtar olarak "yakut" kullandığınız bir karma .

"ruby"değişmez bir dizedir, yani değer budur. Hafıza adresi veya işaretçi sizin için mevcut değil. Her çağırışınızda "ruby", bunun yeni bir örneğini, yani aynı değeri içeren yeni bir hafıza hücresi oluşturuyorsunuz - "ruby".

Karma ardından "benim anahtar değeri ne? Ah öyle gider "ruby". Sonra o değeri eşler 'Başka bir deyişle, kırmızı' ya da 'programlama'. :rubyİçin KQUEUE vermez "red"ya "programming". Karma eşler :ruby için "red"ya "programming".

Bunu, semboller kullanmamızla karşılaştırın

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

Değeri :rubyde "ruby"etkili.

Neden? Çünkü semboller esasen dizge sabitleridir . Sabitler birden fazla örneğe sahip değildir. Aynı hafıza adresi. Ve bir bellek adresinin referansı bir kez başvurulduğunda belirli bir değeri vardır. Semboller için, işaretçi adı semboldür ve başvurulan değer, bu durumda sembol adıyla eşleşen bir dizedir "ruby".

Bir karma içindeyken, sembolü, işaretçiyi değil, ertelenen değeri kullanıyorsunuzdur. Kullanmıyorsun :rubyama "ruby". Karma daha sonra anahtarı arar "ruby", değer "red"veya "programming", karmayı nasıl tanımladığınıza bağlı olarak.

Paradigma kayması ve eve götürme kavramı, bir sembolün değerinin, hash anahtarına verilen bir hash ile eşlenen bir değerden tamamen ayrı bir kavram olmasıdır.


1
Olumsuz oy verenler bu açıklamadaki yanlış veya yanlış nedir? öğrenmek uğruna meraklı.
ahnbizcad

bir benzetmenin bazıları için tatsız olması, onun kusurlu olduğu anlamına gelmez.
ahnbizcad

3
Mutlaka herhangi bir hata görmüyorum, ancak bu cevapta söylemeye çalıştığınız şeyi tespit etmek son derece zor.
Ters Engineered

x, yorumlamayı yapan bağlama / varlığa dayalı olarak farklı şekilde araya girilir ve kavramsallaştırılır. oldukça basit.
ahnbizcad

1
cevabı elden geçirdi. Geri dönüşünüz için teşekkür ederiz.
ahnbizcad
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.