İçeriğe geç
academia.sh

Ders 06 / 12

Semboller ve Dizgiler

Dizginin sembole dönüşmesi, iki temsilin kimlik farkı; sihirli yorumla dondurulan dizgi değişmezleri ve yorumun dosya başına etki etmesi; sihirli yorumun yanıtlayanı değiştirmemesi.

Yayın Güncelleme

Bu derste

  • Dizginin to_sym ile sembole dönüştüğünü ve iki temsilin kimliğinin neden farklı olduğunu equal? ile gösterir
  • Sihirli yorumun dizgi değişmezlerini dondurduğunu frozen? ve FrozenError ile ölçer
  • Sihirli yorumun yanıtlayanı değiştirmediğini, yorumun dosya başına etki ettiğini gösterir
İçindekiler

Önceki ders alıcısız çağrının yanıtlayanını ölçtü ve bir soru bıraktı: "ad".to_sym yazıldığında dizgi bir sembole dönüşüyor, ve sembol de bir nesne. Dizgi sembole dönüşünce iletiyi kim yanıtlıyor? Ayrıca bir dosyanın başına yazılan sihirli yorum bu yanıtı değiştiriyor mu? JavaScript Temelleri kursunda (M06/K01) ilkel tipler ve sembol ölçülmüştü; Ruby’de sembol ayrı bir ilkel değil, Symbol sınıfının bir örneğidir. Dizgi ise değiştirilebilir bir nesnedir, ve bu ders o iki nesnenin farkını ölçer.

M11/K03’te “bu değişikliği kim görüyor” sorusu ölçülmüştü. Bu derste değişebilir dizgi yalnız bir gözlem konusudur; iki adın aynı dizgiyi paylaştığı tek bir örnek görülür, ama dersin ekseni paylaşım değil yanıtlayandır.

Dizgiden Sembole

Sembol, ismi olan değişmez bir değerdir: :ad. Dizgiden to_sym ile elde edilir. Aşağıdaki dosya dönüşümü, iki temsilin kimliğini ve sembolün kendi yöntemlerini ölçer. Kimlik için equal? kullanılır: iki ifade aynı nesneyi mi gösteriyor.

# sembole_donusum.rb — dizgi sembole donusur
puts "ad".method(:to_sym).owner
p "ad".to_sym
puts :ad.equal?(:ad)
puts "ad".equal?("ad")
p :ad.to_s
p :ad.upcase
puts :ad.method(:to_s).owner
puts :ad.method(:upcase).owner
String
:ad
true
false
"ad"
:AD
Symbol
Symbol

to_sym dizginin kendi sınıfında tanımlıdır ve dönüş değeri :ad olarak yazdırılır. Asıl fark kimlikte: :ad.equal?(:ad) true döndü, yani aynı adlı sembol her zaman aynı nesnedir. "ad".equal?("ad") ise false: iki dizgi değişmezi, içerikleri aynı olsa da iki ayrı nesnedir. :ad.to_s yeniden bir dizgi üretir, :ad.upcase ise yeni bir sembol; ikisini de Symbol sınıfı yanıtlar.

DT15. Bu derste “dizgi değişmezi”, kaynak kodda tırnak içinde yazılan dizgidir. Her değerlendirmede yeni bir dizgi nesnesi üretip üretmediği ölçülür. Adres ya da object_id basılmaz; yalnız equal? ile kimlik karşılaştırılır.

Dizgi İşlemleri

Dizgi çok sayıda yöntem taşır. Aşağıdaki dosya uzunluk, büyük harfe çevirme, dilimleme, ayırma, birleştirme, tekrarlama ve biçimleme işlemlerini bir dizgi üzerinde dener. Tanımlayıcılar ve yazdırılan dizeler ASCII tutuldu.

# dizgi_islemleri.rb — dizgi yontemleri
metin = "merhaba"

puts metin.length
puts metin.upcase
puts metin[0, 3]
p "a,b,c".split(",")
puts metin + " dunya"
puts "ad" * 3
puts "%s-%03d" % ["kod", 7]
puts "ad".to_sym.class
puts :ad == "ad"
7
MERHABA
mer
["a", "b", "c"]
merhaba dunya
adadad
kod-007
Symbol
false

length yedi, upcase büyük harfli kopyayı, [0, 3] ilk üç karakteri verir. split bir dizi döndürür, + iki dizgiyi birleştirir, * tekrarlar, % ise biçimler. Bu işlemler metin’i değiştirmek için değil, yeni bir değer üretmek için çağrıldı; length bir sayı, split bir dizi, diğerleri yeni bir dizgi döndürdü. Son iki satır iki temsilin sınırını gösterir: "ad".to_sym bir Symbol verir ve :ad == "ad" false döner; sembol ile aynı adı taşıyan dizgi birbirine eşit sayılmaz.

Karakter ve Bayt

Dizgi bir karakter dizisidir, ama bellekte bayt olarak tutulur. Türkçe harfler tek karakter olduğu hâlde birden çok bayt tutar. Bu ölçümde Türkçe harf bilerek kullanılır, çünkü ölçümün konusu tam olarak budur.

# karakter_bayt.rb — karakter sayisi ile bayt sayisi ayri seylerdir
puts "ig".length
puts "ig".bytesize
puts "ığ".length
puts "ığ".bytesize
2
2
2
4

ASCII harflerden oluşan "ig" iki karakter ve iki bayttır. "ığ" ise iki karakter kalır ama dört bayta çıkar, çünkü ı ve ğ ikişer bayt tutar. Yani length karakter, bytesize bayt sayar; ikisi ASCII dışı metinde ayrışır. Sembolün kodlaması burada ölçülmedi; bu ders sembolün içeriğini değil, kimliğini ölçer.

Sembol Yazımları

Sembol yazmanın birden çok yolu vardır. Boşluk içeren ad tırnakla yazılır, birden çok sembolü bir arada yazmak için %i[...] kısayolu kullanılır, ifade yerleştirme dizgi gibi sembolde de çalışır.

# sembol_yazimlari.rb — sembol yazmanin yollari
p :"ad soyad"
p %i[bir iki uc]
isim = "gul"
p :"onek_#{isim}"
p :ad <=> :ba
p %i[uc bir iki].sort
:"ad soyad"
[:bir, :iki, :uc]
:onek_gul
-1
[:bir, :iki, :uc]

:"ad soyad" tırnaklı yazımdır ve yazdırıldığında tırnağı korur, çünkü ad boşluk içerir. %i[...] üç sembollük bir dizi üretir. :"onek_#{isim}" ifade yerleştirmeyle sembolü çalışma anında kurar. Semboller sıralanabilir: <=> adların karakter sırasına göre -1 verir ve sort diziyi buna göre dizer. Sembol yine bir nesnedir; bu yazımlar ona yalnız farklı bir kapı açar.

Sihirli Yorum

Bir dosyanın en başına yazılan # frozen_string_literal: true satırı sihirli yorum olarak adlandırılır. Dosyadaki dizgi değişmezlerini dondurur. Aşağıdaki dosyada yorum birinci satırdadır, dosya adını taşıyan satır ikinci olur.

# frozen_string_literal: true
# yorumlu.rb — sihirli yorum dizgi degismezlerini dondurur
puts "ad".frozen?
puts "ad".equal?("ad")

metin = "ad"
begin
  metin << "x"
rescue FrozenError => e
  puts e.class
end

puts "ad".dup.frozen?
acik = +"ad"
puts acik.frozen?
true
true
FrozenError
false
false

Yorum yürürlükte iken "ad".frozen? true oldu ve iki ayrı "ad" değişmezi aynı nesne çıktı (equal? true): değişmezler dondurulduğu için Ruby aynı içeriği tek nesnede paylaşabilir. Dizgiye << ile ekleme yapmak FrozenError fırlattı; yakalanan istisnanın yalnız sınıfı basıldı. dup donmuş bir kopya vermez, +"ad" de dondurulmamış bir dizgi döndürür; ikisi de false verdi. Donmuş dizgi donmuş kalır; değiştirilebilir bir dizgi gerekiyorsa onun kopyası alınır.

Sembol ile Dizginin Farkını Ölçmek

Önceki bölümler sembol ile dizginin yazımlarını ve değiştirilebilirliğini ölçtü. Bu bölüm ikisinin birbirine dönüşmesini ve kimlik davranışlarını yan yana koyar. Eksen sahiptir: to_s ve << iletilerini kim karşılıyor?

# gidis_donus.rb — sembol ile dizgi arasinda gidis donus
s = :ad
puts s.to_s
puts s.to_s.to_sym == s
puts s.equal?(:ad)
puts "ad".equal?("ad")
puts "ad".to_sym.equal?(:ad)
ad = "Ayse"
puts "Merhaba #{ad}"
puts 'Merhaba #{ad}'
puts "Merhaba #{ad.size + 1}"
puts s.method(:to_s).owner
ad
true
true
false
true
Merhaba Ayse
Merhaba #{ad}
Merhaba 5
Symbol

:ad.to_s "ad" verdi ve to_sym ile geri dönüş aynı sembolü üretti: s.to_s.to_sym == s true. Bu örnekte dönüşüm gidiş dönüşte bilgi kaybetmedi. Kimlik ise ayrı bir sorudur. s.equal?(:ad) true verdi: aynı sembol yazımı her yerde aynı nesnedir. "ad".equal?("ad") ise false verdi: iki dizgi değişmezi aynı metni taşısa da iki ayrı nesnedir. "ad".to_sym.equal?(:ad) yine true oldu: dizgiden sembole geçince kimlik sembolün kimliğidir. Bu üç ölçüm sembol ile dizgi arasındaki işbölümünü gösterir: sembol kimlik için, dizgi metin için kullanılır. Bir adı etiket olarak tutacaksak sembolün her yerde aynı nesne olması işimize yarar; metni değiştirecek ya da biçimlendirecekse dizgi gerekir.

İfade yerleştirme çift tırnakta çalıştı: "Merhaba #{ad}" Merhaba Ayse basarken, tek tırnaklı 'Merhaba #{ad}' metni #{ad} dahil olduğu gibi bastı. "#{ad.size + 1}" içinde bir ifade çalıştı ve 5 yerleştirildi, çünkü Ayse dört harflidir. Son satırda to_s iletisinin sahibi Symbol çıktı: sembolün dizgiye dönüşümünü sembolün kendi sınıfı karşılar. İfade yerleştirmenin tek tırnakta çalışmaması iki yazımı birbirinin yerine koymayı engeller: değişkenin değerini metne katmak isteyen çift tırnak, metni olduğu gibi tutmak isteyen tek tırnak seçer. Yerleştirilen ifade bir sayı da olabilir; Merhaba 5 çıktısı bunu gösterir.

# yerinde_yeni.rb — << yerinde, + yeni nesne; Turkce harf olcusu
a = +"ad"
b = a
a << "x"
puts b
c = a + "y"
puts c
puts a.equal?(b)
puts a.equal?(c)
puts "ı".upcase
puts "İ".downcase.size
puts "ı".size
puts "ı".bytesize
puts a.method(:<<).owner
adx
adxy
true
false
I
2
1
2
String

a << "x" yerinde çalıştı: a ile aynı nesneyi gösteren b de adx oldu. a + "y" ise yeni bir dizgi üretti ve a’yı değiştirmedi; c adxy oldu. a.equal?(b) true, a.equal?(c) false verdi: << aynı nesneyi değiştirir, + yeni nesne döndürür. <<’un sahibi son satırda String çıktı. Dosya +"ad" ile başladı, çünkü bu yazım değiştirilebilir bir dizgi verir; bu blokta sihirli yorum yoktur.

Türkçe harfler ayrı bir ölçüm yapar. "ı".upcase I verdi. "İ".downcase.size ise 2 verdi: tek harf iki karaktere dönüştü. Beklenen sonuç tek karakterli i olsaydı boyut 1 olurdu. "ı".size 1, "ı".bytesize 2 çıktı: bir karakter, iki bayt. Karakter sayısı ile bayt sayısı bu yüzden birbirinin yerine kullanılamaz. Bu iki ölçümün ortak yönü, dizgiyi düz bir karakter dizisi gibi düşünmenin sınırını göstermesidir. size karakter sayar, bytesize bayt sayar; upcase ve downcase ise tek harfi tek harfe çevirmek zorunda değildir. Bir dizginin uzunluğunu ya da bir alana sığıp sığmadığını hesaplayan kod, hangi birimi saydığını bilmelidir.

DT16. "İ".downcase sonucunun iki karakter olması bu sürümün varsayılan harf eşlemesinin sonucudur. Başka bir sürümde ya da yerelde aynı olduğu ölçülmedi.

Biçimlendirme ve Karşılaştırma

Dizgi yalnız birleştirilmez ve değiştirilmez; biçimlendirilir ve sıralanır da. Bu bölüm dizgiyi biçim belirtimiyle yazar, hizalar, çoğaltır, bir sonraki dizgiye geçirir, karşılaştırır ve harf ile kod noktası arasında çevirir. Eksen sahiptir: sahibi basılan üç iletiden ikisinin sahibi String, birininki Kernel çıkıyor.

# bicimleme.rb — dizgiyi bicimlemek ve karsilastirmak
puts "%05.1f" % 3.14159
puts format("%-4s|", "ab")
puts "ab".center(6, "*")
puts "ab" * 3
puts "az".succ
puts "a" <=> "b"
puts "Ab".casecmp?("aB")
puts "a".ord
puts 98.chr
puts "ab".method(:center).owner
puts "ab".method(:%).owner
puts method(:format).owner
003.1
ab  |
**ab**
ababab
ba
-1
true
97
b
String
String
Kernel

"%05.1f" % 3.14159 ifadesi 003.1 verdi: %05.1f biçimi sayıyı toplam beş genişlikte, bir ondalıkla ve başı sıfırla doldurarak yazdı. format("%-4s|", "ab") aynı belirtim dilini kullanır; - işareti metni sola yaslar ve ab | çıktı. İkisinin sahipleri farklıdır: % String’in yöntemidir, format ise Kernel’in. Aynı biçim dili iki farklı kapıdan çağrılabilir. "ab".center(6, "*") iki yana yıldız ekleyerek **ab** verdi ve "ab" * 3 dizgiyi üç kez art arda yazdı: ababab.

"az".succ ba verdi: bir sonraki dizgi, son harften başlayarak sıralanan değerdir ve z’den sonra a gelip bir sol basamağa taşar. "a" <=> "b" -1 verdi; sıralama iletisi sayı olarak bildirir. "Ab".casecmp?("aB") büyük küçük harfi yok sayarak true döndürdü. "a".ord 97 verdi, 98.chr ise b döndürdü; iki yön birbirinin tersidir. Sahip satırları center ve % için String gösterdi; alıcının dizgi olduğu bu iki işte yanıtlayan dizginin kendi sınıfıdır. succ, <=>, casecmp? ve ord için sahip basılmadı. Bir dizgiyi biçimlendirmek için ayrı bir yardımcı sınıfa ihtiyaç yoktur: dizgi, hizalamayı, çoğaltmayı ve biçim belirtimini kendi yöntemleri olarak taşır.

Kodlama ve Bayt Gerçeği

Bir dizginin karakter sayısı ile bayt sayısı farklı olabilir; bu fark dizginin kodlamasından gelir. Bu bölüm kodlamayı doğrudan sorar: kodlamanın adı, bir harfin kod noktası, geçerlilik ve ham bayt görünümü. Eksen sahiptir; bayt sorgusunu hangi sınıfın karşıladığı sonda basılır.

# kodlama.rb — kodlama, kod noktasi ve bayt
puts "a".encoding
puts "ı".bytes.inspect
puts "ı".ord
puts 305.chr(Encoding::UTF_8)
puts "ı".valid_encoding?
puts "\xff".valid_encoding?
puts "ı".unpack("U*").inspect
puts "ı".b.size
puts "a".method(:bytes).owner
UTF-8
[196, 177]
305
ı
true
false
[305]
2
String

Kaynak dosyadaki bir dizginin kodlaması UTF-8 çıktı. "ı".bytes iki bayt verdi, [196, 177]; "ı".ord ise harfin kod noktasını, 305’i verdi. Bayt ile kod noktası iki ayrı sayıdır: kod noktası harfi, baytlar harfin kodlanmış biçimini tanımlar. 305.chr(Encoding::UTF_8) kod noktasından harfi geri üretti ve ı basıldı. Böylece ord ile chr bir gidiş dönüş kurar.

Geçerlilik ölçümü iki sonuç verdi. "ı".valid_encoding? true oldu, "\xff".valid_encoding? ise false: tek başına 0xff baytı UTF-8 kodlamasında geçerli bir harf oluşturmaz. "ı".unpack("U*") [305] döndürdü; yani aynı harf iki baytın birleşimiyle tek bir kod noktasına çözülür. "ı".b.size ise 2 verdi: b dizgiyi ham bayt görünümüne çevirdiği için boyut karakter sayısını değil bayt sayısını gösterdi. Önceki bölümdeki size ve bytesize farkı burada kodlama düzeyinde yeniden ortaya çıkar. Sahip String çıktı: bayt sorgusunu da dizginin kendi sınıfı karşılar.

Sınırlayıcı Ölçüm

Bu dersin yanıtlayanı sınıftır. Burada iki kayma olabilir. Birincisi: sihirli yorum iletinin yanıtlayanını değiştirir sanılabilir. İkincisi: yorum bir kez yazılınca tüm programı etkiler sanılabilir. İkisi de yanlıştır; ölçüm iki dosyayı yan yana koyar. Önce yorumsuz bir dosya diske yazılır, sonra yorumlu ana dosya onu require_relative ile çeker.

# yorumsuz.rb — sihirli yorum yok
def yorumsuz_dondurulmus
  "ad".frozen?
end

def yorumsuz_sahip
  "ad".method(:upcase).owner
end
# frozen_string_literal: true
# ana.rb — yorum dosya basinadir
require_relative "yorumsuz"

puts "ad".method(:upcase).owner
puts yorumsuz_sahip
puts "ad".frozen?
puts yorumsuz_dondurulmus
String
String
true
false

"ad".upcase iletisinin sahibi iki dosyada da aynıdır: String. Yorum yanıtlayanı değiştirmedi; değişen, değişmezin üretimidir. Yorumlu dosyada "ad".frozen? true, yorumsuz dosyada false kaldı, oysa ikisi aynı süreçte çalıştı. Yorum dosya başına etki eder: bir dosyadaki yorum, o dosyanın çektiği başka bir dosyanın değişmezlerini dondurmaz.

Özet

  • "ad".to_sym dizgiyi sembole çevirir ve dönüşü String sınıfı yanıtlar; :ad.to_s ve :ad.upcase ise Symbol sınıfındadır.
  • Aynı adlı sembol her zaman aynı nesnedir (:ad.equal?(:ad) true); iki dizgi değişmezi içerik olarak aynı olsa da farklı nesnedir.
  • # frozen_string_literal: true sihirli yorumu dizgi değişmezlerini dondurur: frozen? true, << ise FrozenError fırlatır.
  • dup ve +"ad" dondurulmamış bir dizgi verir; donmuş dizginin kendisi donmuş kalır.
  • Bu dersin yanıtlayanı çoğunlukla sınıftır (String, Symbol; format için Kernel modülü): sihirli yorum iletiyi yanıtlayanı değiştirmez ve yalnız bulunduğu dosyadaki değişmezleri etkiler.
  • size karakteri, bytesize baytı sayar ("ı" için 1 ve 2); << yerinde değiştirir, + yeni dizgi üretir; ord ile chr kod noktası için gidiş dönüş kurar.

Sonraki Adım

Dizgiler ve semboller ölçüldü; ama yanıtlayanın kim olduğu hep tek değerli nesneler için soruldu. Bir koleksiyonda, örneğin bir karma yapıda, olmayan bir anahtar sorulduğunda cevabı kim veriyor? Sonraki ders, Diziler ve Karma Yapılar, eksik anahtarın değerini kimin sağladığını ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat