Ruby'de Symbol bir tanımlayıcıyı (adı) temsil eder, String ise metin ya da veri taşır. En önemli fark kimliktir: aynı adlı sembol program çalıştığı sürece her yerde aynı nesnedir (:ad.object_id hep aynıdır), aynı içerikli iki string ise genellikle iki ayrı nesnedir. Semboller değiştirilemez; string'ler varsayılan olarak değiştirilebilir. Birinden diğerine to_sym ve to_s ile geçersiniz. Hash anahtarlarında ve metot adlarında sembol kullanmak Ruby'nin yerleşik alışkanlığıdır.

Bu yazıda iki türü kimlik, dönüşüm, hash anahtarı ve değiştirilebilirlik açısından karşılaştırıyor, her birini ne zaman seçeceğinizi örneklerle gösteriyoruz. String'in kendi metotları String metotları yazısında ayrıca anlatılıyor. Seriye baştan başlamak için Ruby öğrenme rehberine bakın.

Sembol nedir, nasıl yazılır?

Sembol nedir, nasıl yazılır?

Sembol iki nokta üst üste ile başlayan bir addır: :durum. Boşluk ya da özel karakter içeren bir ad için tırnak kullanılır ve çift tırnakta gömme yapılabilir. Sembol dizileri için %i[] yazımı vardır. Ruby'nin kendisi de sabit, metot ve değişken adlarını size sembol olarak verir.

ruby
:durum.class # => Symbol
:"ad soyad" # => :"ad soyad"
:"kayit_#{1 + 1}" # => :kayit_2
%i[ad soyad yas] # => [:ad, :soyad, :yas]
:şehir.inspect # => ":şehir"

class Kisi
  attr_accessor :ad
end
Kisi.instance_methods(false).sort # => [:ad, :ad=]
"abc".respond_to?(:upcase) # => true

Belgelerdeki örnekte de görüldüğü gibi :Fred sembolü, Fred bir yerde sınıf, başka bir yerde sabit, bir başka yerde metot adı olsa bile hep aynı nesnedir. Sembol bir "ad"dır; o adın ne anlama geldiği bağlama bağlıdır.

Kimlik: object_id ve equal?

== içeriği karşılaştırır, equal? ise iki ifadenin aynı nesne olup olmadığına bakar. Semboller için iki soru aynı cevabı verir. String literalleri ise (dosyada # frozen_string_literal: true yorumu yoksa) her değerlendirildiğinde yeni bir nesne oluşturur:

ruby
:durum.object_id == :durum.object_id # => true
:durum.equal?(:durum) # => true
"durum" == "durum" # => true
"durum".equal?("durum") # => false
"durum".object_id == "durum".object_id # => false
(-"durum").equal?(-"durum") # => true

Son satırdaki tekli eksi (-@, diğer adı dedup), dondurulmuş ve tekilleştirilmiş bir string döndürür; belgelere göre aynı içerikli iki string bu sayede aynı nesneye indirgenebilir. Yani string'lerde de tek nesne elde edebilirsiniz, ama bunu açıkça istemeniz gerekir. Sembollerde bu davranış dilin kendisinde vardır.

Dönüşüm: to_sym, to_s, name

String#to_sym (diğer adı intern) karşılık gelen sembolü döndürür, sembol daha önce yoksa oluşturur. Symbol#to_s (diğer adı id2name) yeni, değiştirilebilir bir string verir. Symbol#name ise dondurulmuş bir string döndürür; Ruby 3.0 NEWS dosyasında eklendiği belirtilir. Yalnızca okuyacaksanız name yeterlidir.

ruby
"ad".to_sym # => :ad
"ad soyad".to_sym # => :"ad soyad"
"şehir".intern # => :şehir
:ad.to_s # => "ad"
:ad.to_s.frozen? # => false
:ad.name # => "ad"
:ad.name.frozen? # => true
:ad.to_sym.equal?(:ad) # => true

Kullanıcıdan gelen rastgele metni sembole çevirmek genellikle gerekmez. Ruby 2.2 NEWS dosyasına göre to_sym ve intern ile üretilen sembollerin çoğu çöp toplayıcı tarafından temizlenebilir; yine de bu tür veriyi string olarak tutmak niyetinizi daha açık gösterir.

Hash anahtarı olarak Symbol ve String

Hash anahtarı olarak Symbol ve String

Ruby'de hash anahtarı için sembol kullanmak yaygındır ve {ad: "Efe"} kısa yazımı yalnızca sembol anahtar üretir. Sembol ve string anahtarlar farklı anahtarlardır: :ad ile "ad" aynı girişe ulaşmaz. Bu, dışarıdan gelen veriyle çalışırken en sık karşılaşılan hatadır.

ruby
h = {ad: "Efe"}
h[:ad] # => "Efe"
h["ad"] # => nil

j = {"ad" => "Efe"}
j[:ad] # => nil
j.transform_keys(&:to_sym) # => {ad: "Efe"}
h.transform_keys(&:to_s) # => {"ad" => "Efe"}
{"ad": 1}.keys.first.class # => Symbol

Son satıra dikkat: {"ad": 1} yazımında tırnak olsa da iki nokta kullanıldığı için anahtar yine semboldür. JSON verisini çözümlerken anahtarlar varsayılan olarak string gelir; JSON.parse metodunun symbolize_names: true seçeneği onları sembole çevirir. Ayrıntı: Ruby JSON kullanımı.

ruby
require "json"
JSON.parse('{"ad": "Efe"}') # => {"ad" => "Efe"}
JSON.parse('{"ad": "Efe"}', symbolize_names: true) # => {ad: "Efe"}

Değiştirilemezlik: Symbol frozen, String değil

Semboller dondurulmuştur ve yerinde değiştiren metotları yoktur. upcase, succ gibi Symbol metotları her zaman yeni bir sembol döndürür. String ise freeze çağrılana kadar değiştirilebilir.

ruby
:ad.frozen? # => true
String.new("ad").frozen? # => false
:merhaba.upcase # => :MERHABA
:merhaba.size # => 7
:merhaba.start_with?("mer") # => true
:a <=> :b # => -1
begin
  :ad << "x"
rescue NoMethodError => e
  puts e.message # çıktı: undefined method '<<' for an instance of Symbol
end

Symbol#to_proc ve &:metot yazımı

map(&:upcase) gibi kısa yazımlar sembolün to_proc metoduna dayanır: sembol, ilk argümanı üzerinde o adlı metodu çağıran bir Proc nesnesine dönüşür. Bu, sembollerin "metot adı" rolünün günlük kodda en sık görüldüğü yerdir.

ruby
%w[elma armut].map(&:upcase) # => ["ELMA", "ARMUT"]
:upcase.to_proc.call("abc") # => "ABC"
"abc".public_send(:upcase) # => "ABC"
[1, 2, 3].sum # => 6
[1, 2, 3].reduce(:+) # => 6

Proc nesneleri ve & operatörünün ayrıntıları proc ve lambda farkı yazısında anlatılıyor.

Ruby'nin size sembol verdiği yerler

Sembolün "ad" rolü, Ruby'nin kendini anlatan (yansıma, reflection) metotlarında açıkça görülür. Bir modülün sabitleri, örnek değişkenleri, yerel değişkenler ve metot adları hep sembol dizisi olarak döner. Tersine, bir metodu adıyla çağırmak ya da tanımlamak için de sembol verirsiniz. Belgelerdeki Symbol sayfası bu örnekleri listeler; aşağıda benzerlerini çalıştırıyoruz:

ruby
module Ayar
  SURUM = 4
  @ortam = "test"
  def self.selam = "merhaba"
end
Ayar.constants # => [:SURUM]
Ayar.instance_variables # => [:@ortam]
Ayar.singleton_methods # => [:selam]
Ayar.public_send(:selam) # => "merhaba"
Ayar.const_get(:SURUM) # => 4
sayi = 7
local_variables.include?(:sayi) # => true
def kare(x) = x * x
method(:kare).call(5) # => 25

Bu metotlar size string değil sembol döndürdüğü için karşılaştırmayı da sembolle yapmalısınız: Ayar.constants.include?("SURUM") false döner. Sınıf ve modül yapıları için class ve nesneler yazısına bakabilirsiniz.

Yaygın hatalar

Yaygın hatalar: Dış veride sembol anahtar beklemek, Yansıma sonuçlarını string ile karşılaştırmak, Sembolü değiştirmeye çalışmak, to_s ile name farkını gözden kaçırmak
  • Dış veride sembol anahtar beklemek: JSON, CSV ya da form verisinden gelen anahtarlar string'tir; veri[:ad] nil döner.
  • Yansıma sonuçlarını string ile karşılaştırmak: methods, constants ve benzerleri sembol döndürür.
  • Sembolü değiştirmeye çalışmak: <<, gsub! gibi metotlar Symbol'de yoktur; önce to_s ile string alın.
  • to_s ile name farkını gözden kaçırmak: name dondurulmuş string döndürür; sonucu değiştirecekseniz to_s kullanın.
  • Her şeyi sembole çevirmek: kullanıcı girdisi, dosya içeriği ve ekrana yazılacak metin string olarak kalmalıdır.

Karşılaştırma tablosu

ÖzellikSymbolString
Ne temsil ederad, tanımlayıcımetin veya veri
Aynı içerik aynı nesne mievet, her zamanhayır (dedup ile istenebilir)
Değiştirilebilir mihayır, frozenevet (freeze edilmedikçe)
Literal:ad, :"ad soyad", %i[]"ad", 'ad', %w[]
Dönüşümto_s, nameto_sym, intern
Tipik kullanımhash anahtarı, metot adı, seçenekkullanıcı metni, dosya içeriği
Gömme (interpolation):"x#{1}" ile"x#{1}" ile

Ne zaman hangisi?

  • Symbol seçin: değer bir "ad" ise (hash anahtarı, durum adı gibi :aktif/:pasif, metot adı, seçenek anahtarı).
  • String seçin: değer kullanıcıdan, dosyadan, ağdan geliyorsa, üzerinde metin işlemi yapacaksanız ya da ekrana yazacaksanız.
  • Tutarlı olun: bir hash'te hem sembol hem string anahtar karıştırmak, okunduğu yerde hangisinin beklendiği konusunda hataya yol açar.
  • Sınırda dönüştürün: dış veriyi programa girerken bir kez dönüştürün (örneğin symbolize_names), içeride tek türle çalışın.

Sık Sorulan Sorular

Sembol kullanmak performansı artırır mı?

Belgeler bunun için bir ölçüm vermez. Kesin olan, aynı adlı sembolün tek nesne olması ve karşılaştırmanın kimlik üzerinden yapılabilmesidir. Seçimi performansa göre değil, değerin "ad" mı "metin" mi olduğuna göre yapmanız daha doğru olur.

Symbol'e String metotları uygulanabilir mi?

Bir kısmı evet: size, upcase, start_with?, [] gibi metotlar Symbol'de de vardır ve belgelere göre çoğu to_s sonucunda aynı metodu çağırmakla eşdeğerdir. Değiştiren metotlar (<<, gsub!) yoktur.

Symbol'ler karşılaştırılabilir ve sıralanabilir mi?

Evet. Symbol, Comparable modülünü içerir ve <=> karşılaştırmasını string karşılığı üzerinden yapar. Bu yüzden %i[c a b].sort sonucu [:a, :b, :c] olur. Sembolle string'i karşılaştırmak ise nil döner, çünkü ikisi farklı türdür.

<code>{"ad": 1}</code> neden sembol anahtar üretiyor?

Hash literalinde iki nokta üst üsteli yazım her zaman sembol anahtar üretir; tırnak yalnızca boşluk ya da özel karakter içeren adları yazmanızı sağlar. String anahtar istiyorsanız {"ad" => 1} yazın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Ruby öğrenme rehberi · Ruby String metotları · Ruby Hash metotları · Frozen string ve freeze · Proc ve lambda farkı · Veri tipleri ve literaller