Ruby'de dizeler yerine semboller ne zaman kullanılır?


100

Komut dizimde aynı dizenin en az iki örneği varsa bunun yerine bir sembol kullanmalı mıyım?

Yanıtlar:


177

TL; DR

Basit bir temel kural, dahili tanımlayıcılara her ihtiyaç duyduğunuzda sembolleri kullanmaktır. Ruby <2.2 için, bellek sızıntılarını önlemek için, sembolleri yalnızca dinamik olarak oluşturulmadıklarında kullanın.

Tam cevap

Bunları dinamik olarak oluşturulan tanımlayıcılar için kullanmamanın tek nedeni bellek endişeleridir.

Bu soru çok yaygındır çünkü birçok programlama dilinde semboller yoktur, yalnızca dizeler vardır ve bu nedenle dizeler kodunuzda tanımlayıcılar olarak da kullanılır. Sen semboller ne hakkında endişe verici olmalıdır olması gerekiyordu sadece, sen sembolleri kullanılmalıdır zaman . Sembollerin tanımlayıcı olması amaçlanmıştır. Bu felsefeyi takip ederseniz, işleri doğru yapma şansınız vardır.

Sembollerin ve dizelerin uygulanması arasında birkaç fark vardır. Sembollerle ilgili en önemli şey, değişmez olmalarıdır . Bu, değerlerini asla değiştirmeyecekleri anlamına gelir. Bu nedenle, semboller dizelerden daha hızlı başlatılır ve iki sembolü karşılaştırmak gibi bazı işlemler de daha hızlıdır.

Bir sembolün değişmez olması gerçeği, Ruby'nin sembole her başvurduğunuzda aynı nesneyi kullanmasına ve hafızadan tasarruf etmesine izin verir. Dolayısıyla, yorumlayıcı her okuduğunda :my_key, yeniden örneklemek yerine onu bellekten alabilir. Bu, her seferinde yeni bir dizge başlatmaktan daha ucuzdur.

Şu komutla halihazırda somutlaştırılmış tüm sembollerin bir listesini alabilirsiniz Symbol.all_symbols:

symbols_count = Symbol.all_symbols.count # all_symbols is an array with all 
                                         # instantiated symbols. 
a = :one
puts a.object_id
# prints 167778 

a = :two
puts a.object_id
# prints 167858

a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!

puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.

2.2'den önceki Ruby sürümleri için, bir sembol somutlaştırıldığında, bu bellek bir daha asla serbest olmayacaktır . Belleği boşaltmanın tek yolu uygulamayı yeniden başlatmaktır. Bu nedenle semboller, yanlış kullanıldığında bellek sızıntılarının önemli bir nedenidir. Bir bellek sızıntısı oluşturmanın en basit yolu, yöntemi to_symkullanıcı girdi verilerinde kullanmaktır, çünkü bu veriler her zaman değişeceğinden, belleğin yeni bir kısmı yazılım örneğinde sonsuza kadar kullanılacaktır. Ruby 2.2 , dinamik olarak oluşturulan sembolleri serbest bırakan sembol çöp toplayıcıyı tanıttı , böylece dinamik olarak semboller oluşturarak üretilen bellek sızıntıları artık bir sorun değil.

Sorunuzu cevaplamak:

Uygulamamda veya komut dosyamda aynı dizelerden en az iki tane varsa, dize yerine bir sembol kullanmam gerektiği doğru mu?

Aradığınız şey kodunuzda dahili olarak kullanılacak bir tanımlayıcıysa, semboller kullanıyor olmalısınız. Çıktı yazdırıyorsanız, birden fazla görünse bile, bellekte iki farklı nesne tahsis etseniz bile dizelerle gitmelisiniz.

Nedeni şu:

  1. Dizelere dönüştürüldükleri için sembollerin yazdırılması dizeleri yazdırmaktan daha yavaş olacaktır.
  2. Birçok farklı sembole sahip olmak, asla ayrılmadıkları için uygulamanızın genel bellek kullanımını artıracaktır. Ve asla kodunuzdaki tüm dizeleri aynı anda kullanmazsınız.

@AlanDert tarafından örnek kullanım

@AlanDert: Haml kodunda birçok kez% input {type:: checkbox} gibi bir şey kullanırsam, onay kutusu olarak ne kullanmalıyım?

Ben evet.

@AlanDert: Ama html sayfasına bir sembol yazdırmak için dizeye dönüştürülmeli, değil mi? o zaman kullanmanın anlamı ne?

Bir girdinin türü nedir? Kullanmak istediğiniz giriş türünün bir tanımlayıcısı mı yoksa kullanıcıya göstermek istediğiniz bir şey mi?

Bir noktada HTML kodu olacağı doğrudur, ancak şu anda kodunuzun o satırını yazarken, bu bir tanımlayıcı anlamına gelir - ne tür bir giriş alanına ihtiyacınız olduğunu tanımlar. Bu nedenle, kodunuzda tekrar tekrar kullanılır ve her zaman tanımlayıcı olarak aynı "karakter dizisine" sahiptir ve bir bellek sızıntısı oluşturmaz.

Bununla birlikte, dizelerin daha hızlı olup olmadığını görmek için neden verileri değerlendirmiyoruz?

Bu, bunun için oluşturduğum basit bir ölçüt:

require 'benchmark'
require 'haml'

str = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: "checkbox"}').render
  end
end.total

sym = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: :checkbox}').render
  end
end.total

puts "String: " + str.to_s
puts "Symbol: " + sym.to_s

Üç çıkış:

# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68

Yani smbols kullanmak aslında dizeleri kullanmaktan biraz daha hızlı. Neden? HAML'nin uygulanma şekline bağlıdır. Görmek için HAML kodunu biraz kırmam gerekecek, ancak bir tanımlayıcı konseptindeki sembolleri kullanmaya devam ederseniz, uygulamanız daha hızlı ve güvenilir olacaktır. Sorular ortaya çıktığında, onu kıyaslayın ve cevaplarınızı alın.


@andrewcockerham Sağladığınız bağlantı çalışmıyor (Hata-404). Bağlantıdan sonuncu /(sonra strings) kaldırmanız gerekir . İşte: www.reactive.io/tips/2009/01/11/the-difference-between-ruby-‌ symbols-and-strings
Atul Khanduri

14

Basitçe ifade etmek gerekirse, bir sembol, karakterlerden oluşan ancak değişmez bir isimdir. Aksine, dizge, içeriklerinin değişmesine izin verilen karakterler için sıralı bir kaptır.


4
+1. Semboller ve Dizeler tamamen farklı şeylerdir. Gerçekten hangi gibi herhangi bir karışıklık bir kullanımı, yok olmadıkça onlar kötü öğretilmiş (yani safsata "sembolü sadece bir değişmez dizedir").
Jörg W Mittag

@ JörgWMittag: Kesinlikle.
Boris Stitnicky

5
bir amacınız var, ancak yapılan soruyu cevaplamayın. OP dizeleri sembollerle karıştırıyor, bunun farklı şeyler olduğunu söylemek yeterli değil - neye benzediklerini ve neleri farklı olduklarını anlamasına yardım etmelisiniz
fotanus

1
@ JörgWMittag, dokümantasyona bakmadığınız veya olayları gerçekte olduğu gibi açıklamaya özen gösteren insanları bulacak kadar şanslı olmadığınız sürece, web'in her yerinde meydana gelen görünüyor.
sargas

5

Codecademy'de bulduğum güzel dizeler ve semboller karşılaştırması:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  1000_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  1000_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

Çıktı:

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.

2
Bunun saniyenin onda biri olduğu gerçeğini unutmayalım.
Casey

Hepsi göreceli. Bazen yüzüncü madde.
Yurii

2
Bir milyondan fazla yineleme saniyenin yüzde biri mi? Kullanabileceğiniz en iyi optimizasyon buysa, programınız zaten oldukça iyi optimize edilmiş demektir.
Casey

0
  • hash anahtarı tanımlayıcıları olarak sembolleri kullanın

    {key: "value"}

  • semboller yöntemi farklı bir sırayla çağırmanıza izin verir

     def write (dosya :, veri :, mod: "ascii")
          # kısalık için kaldırıldı
     son
     yazma (veri: 123, dosya: "test.txt")
  • dize olarak tutmak ve hafızadan tasarruf etmek için dondurun

    label = 'My Label'.freeze

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.