Ruby Koans: Sembol listesini neden dizelere dönüştürmelisiniz?


86

Ruby Koans'ta about_symbols.rb'de bu teste atıfta bulunuyorum https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26

def test_method_names_become_symbols
  symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
  assert_equal true, symbols_as_strings.include?("test_method_names_become_symbols")
end


  # THINK ABOUT IT:
  #
  # Why do we convert the list of symbols to strings and then compare
  # against the string value rather than against symbols?

Neden önce bu listeyi dizelere dönüştürmemiz gerekiyor?

Yanıtlar:


112

Bunun, sembollerin nasıl çalıştığı ile ilgisi var. Her sembol için, aslında sadece biri var. Perde arkasında bir sembol, bir isimle anılan bir sayıdır (iki nokta üst üste ile başlayan). Bu nedenle, iki sembolün eşitliğini karşılaştırırken, bu sembole atıfta bulunan tanımlayıcının içeriğini değil , nesne kimliğini karşılaştırırsınız .

Basit testi yapacaksanız : test == "test" , yanlış olacaktır. Öyleyse, şimdiye kadar tanımlanan tüm sembolleri bir dizi halinde bir araya getirecekseniz, onları karşılaştırmadan önce onları dizelere dönüştürmeniz gerekir. Bunu tam tersi şekilde yapamazsınız (önce karşılaştırmak istediğiniz dizeyi bir sembole dönüştürün) çünkü bunu yapmak o sembolün tek bir örneğini oluşturur ve varlığını test ettiğiniz sembolle listenizi "kirletir".

Umarım yardımcı olur. Bu biraz garip, çünkü test sırasında yanlışlıkla o sembolü oluşturmadan bir sembolün varlığını test etmeniz gerekiyor. Genellikle böyle bir kod görmezsiniz.


2
Bunu güvenli bir şekilde yapmanın daha iyi bir yolunun, çıkışını Symbol.all_symbolsbir değişkene atamak ve ardından dahil etmeyi test etmek olduğunu unutmayın. Semboller karşılaştırıldığında daha hızlıdır ve binlerce sembolü dizelere dönüştürmekten kaçınırsınız.
coreyward

4
Hâlâ yok edilemeyen sembolü yaratma sorunu var. Bu sembol için gelecekteki tüm testler mahvolacak. Ama bu sadece bir Koan, çok mantıklı ya da hızlı olması gerekmiyor, sadece sembollerin nasıl çalıştığını göster.
AboutRuby

2
Bu cevap benim için işe yaramıyor. Biz sembolün varlığının araması yapıyorsanız, neden bir dize argüman belirtiyorsanız include?biz belirtilmiş ise :test_method_names_become_symbolsbiz dizeleri tüm bu sembolleri dönüştürmek olmazdı.
Isaac Rabinovitch

3
Isaac, tanımlanan sorun nedeniyle :test_method_names_become_symbols, karşılaştırmada belirtmenin onu yaratacağı, dolayısıyla karşılaştırma her zaman doğru olacaktır. Dizelere dönüştürerek all_symbolsve dizeleri karşılaştırarak, sembolün karşılaştırmadan önce var olup olmadığını ayırt edebiliriz.
Stephen

3
O kadar çok anlamıyorum ki. >> array_of_symbols = Symbol.all_symbols, sonra >> array_of_symbols.include? (: Not_yet_used) yaparsam, yanlış alırım ve tanımlanan bir şey için doğru olurum, bu nedenle dizelere dönüştürmenin neden gerekli olduğunu anlamıyorum .
codenoob

75

Çünkü yaparsan:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

:test_method_names_become_symbolsSembolü yarattığı için (Ruby uygulamanıza bağlı olarak) otomatik olarak doğru olabilir . Bu hata raporuna bakın .


1
Yani kabul edilen cevap yanlış cevaptır (ya da bana öyle geliyor).
Isaac Rabinovitch

3
Isaac, diğer cevap yanlış değil ama kısaca açıklanmadı. kesinlikle. Her neyse, Andrew'un söylediklerini aşağıdakilerle doğrulayabilirsiniz: assert_equal true, Symbol.all_symbols.include? (: Abcdef) Bu, sembol ne olursa olsun her zaman geçer (en azından benim için geçer). Sanırım bir ders, sembollerin varlığını / yokluğunu bir boole bayrağı olarak kullanmaya çalışmayın.
Stephen

1
Hala ilgimi çeken bu soruya 2 yıl sonra baktığımda, bu cevabı ve yorumları gerçekten takdir ediyorum. Bence Isaac haklı, bu, neden dizelere dönüştürmenin en iyi yolu olabileceğini açıklayan cevaptır, ancak bence daha iyi çalışmasını sağlamak için ara adımı (karşılaştırmadan önce tüm sembolleri saklayın) koyabilirsiniz.
codenoob

4

Yukarıdaki her iki cevap da doğrudur, ancak yukarıdaki Karthik'in sorusu ışığında, bir sembolü includeyönteme nasıl doğru bir şekilde geçirebileceğini gösteren bir test yayınlayacağımı düşündüm.

def test_you_create_a_new_symbol_in_the_test
  array_of_symbols = []
  array_of_symbols << Symbol.all_symbols
  all_symbols = Symbol.all_symbols.map {|x| x}
  assert_equal false, array_of_symbols.include?(:this_should_not_be_in_the_symbols_collection)  #this works because we stored all symbols in an array before creating the symbol :this_should_not_be_in_the_symbols_collection in the test
  assert_equal true, all_symbols.include?(:this_also_should_not_be_in_the_symbols_collection) #This is the case noted in previous answers...here we've created a new symbol (:this_also_should_not_be_in_the_symbols_collection) in the test and then mapped all the symbols for comparison. Since we created the symbol before querying all_symbols, this test passes.
end

Koanlar hakkında ek bir not: putsHiçbir şey anlamadıysanız , ifadelerden ve özel testlerden yararlanın. Örneğin, şunu görürseniz:

string = "the:rain:in:spain"
words = string.split(/:/)

ve ne wordsolabileceği hakkında hiçbir fikriniz yok , satırı ekleyin

puts words

ve rakekomut satırında çalıştırın . Benzer şekilde, yukarıda eklediğim gibi testler Ruby'nin bazı nüanslarını anlamak açısından yardımcı olabilir.


Hala ilgimi çeken bu soruya 2 yıl sonra baktığımda, bu cevabı ve yorumları gerçekten takdir ediyorum.
codenoob
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.