İçeriğe geç
academia.sh

Ders 03 / 12

Değişkenler ve Sabitler

Ruby'de atamanın bir ileti değil sözdizimi olması; yerel değişken, örnek değişkeni, genel değişken ve sabitin ad işaretiyle ayrılması; defined? ile ölçülen sınırlar.

Yayın Güncelleme

Bu derste

  • Yerel değişken, örnek değişkeni, genel değişken ve sabiti kapsam işaretleriyle ayırt eder
  • Atamayı defined? ile ölçerek bir ileti değil sözdizimi olduğunu gösterir
  • Birleşik atamanın (+=, ||=) atama, içindeki toplamanın ise ileti olduğunu ayırır
İçindekiler

Önceki ders her değerin bir sınıfa ait bir nesne olduğunu ve iletiyi alıcının sınıfının yanıtladığını kurdu. Bir ad bu nesnelerden birine nasıl bağlanıyor sorusu açık kaldı: x = 5 yazıldığında x’e bir ileti mi gidiyor, 5‘e mi, yoksa ortada hiç ileti yok mu? Değişken kavramının kendisi Programlama Temelleri kursunda (M01/K02) anlatılmıştı; burada o kavram tekrarlanmaz. Bu ders yalnız Ruby’ye özgü soruyu ölçer: ad nesneye bağlanırken kim çalışır?

Ruby’de bir adın ne olduğunu başındaki işaret belirler. Bu işaretlere kapsam işareti diyoruz: işaretsiz küçük harfle başlayan ad yerel değişkendir, @ ile başlayan örnek değişkeni, $ ile başlayan genel değişken, büyük harfle başlayan ad sabittir. @@ ile başlayan sınıf değişkeni de vardır; ona Kapsam Sınırları ve Paralel Atama bölümünde dönülür. Aşağıdaki dosya dört işareti, tanımsız bir sabiti ve bir atamayı defined? ile ayrı ayrı sorgular.

Ad İşareti Türü Belirler

defined? bir ifadeyi çalıştırmadan onun ne olduğunu söyleyen bir deyimdir. Tanımsız bir ad için nil döndürür, tanımlı için ne olduğunu belirten bir dizgi. Bu, derste kullanacağımız ölçü aletidir.

# kapsam_isaretleri.rb — ad isareti degiskenin turunu belirler
x = 5
@a = 1
$gz = 2

puts defined?(x)
puts defined?(@a)
puts defined?($gz)
puts defined?(String)
p defined?(Yok)
puts defined?(sonuc = 5)
local-variable
instance-variable
global-variable
constant
nil
assignment

Altı sorgu, altı farklı yanıt: x yerel değişken, @a örnek değişkeni, $gz genel değişken, String sabit. Tanımsız sabit Yok için nil döndü. Son satır asıl konudur: sonuc = 5 ifadesi "assignment" olarak bildirildi. Ne "method" ne "expression"; atamanın kendi adı var.

DT7. Bu derste “atama” = ile bir adı bir nesneye bağlayan yazımdır. defined? yalnız ifadenin türünü okur, atamayı gerçekten çalıştırmaz; bu yüzden yukarıdaki sonuc adı hiç oluşmadı. Sabit adlarına yeniden atamanın uyarı bastığı bilinir, ama o uyarı bir dosya yolu taşıdığı için burada basılmaz ve ölçülmez; yalnız düzyazıda anılır.

Sabit Adı Bağlar, Nesneyi Dondurmaz

Büyük harfle başlayan bir ad sabittir. Sabitin tanımlı olduğu Object.const_get ile okunabilir. Peki sabit neyi sabitler: adın bağlandığı nesneyi mi, adın bağlanmasını mı? Aşağıdaki dosya bir sayıyı ve bir diziyi birer sabite bağlar, sonra diziye bir eleman ekler.

# sabit_dizi.rb — sabit adi baglar, nesneyi dondurmaz
SINIR = 10
LISTE = [1]

puts Object.const_get(:SINIR)
puts defined?(SINIR)
LISTE << 2
p LISTE
puts LISTE.frozen?
puts SINIR.frozen?
10
constant
[1, 2]
false
true

Dizi LISTE sabitine bağlı olduğu hâlde değişti: sabit adın başka bir nesneye bağlanmasını uyarıyla işaretler, nesnenin içeriğini kilitlemez. Dizi dondurulmamıştır (false). Sayılar ise zaten değiştirilemez nesnelerdir ve frozen? onlar için true döner. Sabiti gerçekten değiştirilemez yapmak isteyen, nesneyi ayrıca dondurur; dondurma bu kursta Semboller ve Dizgiler dersinde dizgiler üzerinde ölçülür.

Her Tür Kendi Yerinde Tutulur

Kapsam işaretleri yalnız yazım kolaylığı değildir; her tür farklı bir yerde saklanır. Yerel değişkenler çalışan kodun kendi kapsamındadır, örnek değişkenleri bir nesneye bağlıdır, genel değişkenler ise programın tamamında görünür. Ruby bu üçünü listeleyen yöntemler sunar. Aşağıdaki dosya üst düzeyde üç türü tanımlar ve her birini kendi listesinde arar.

# degisken_listeleri.rb — her tur kendi listesinde tutulur
x = 5
@a = 1
$gz = 2

p local_variables
p instance_variables
puts global_variables.include?(:$gz)
puts self.inspect
puts self.class
[:x]
[:@a]
true
main
Object

Yerel değişken listesinde x, örnek değişkeni listesinde @a bulundu; $gz ise genel değişkenler listesinde. Örnek değişkeninin kimin üzerinde durduğu sorusunun cevabı self’tir: üst düzeyde self dosyanın ana nesnesidir, main olarak yazdırılır ve sınıfı Object’tir. Yani üst düzeyde yazılan @a, bir Object örneğine bağlanmıştır; örnek değişkeni her zaman bir nesnenin değişkenidir.

Atama Bir İleti Değil

Önceki dersin kuralı şuydu: 5 + 3 bir iletidir ve yanıtlayanı sınıftır. Atama için aynı soru sorulabilir: x = 5 bir iletiyse, onu yanıtlayan bir sahip bulunmalıdır. Aşağıdaki dosya üç şeyi yan yana koyar: düz atamayı, değer döndüren yöntem çağrısını ve atamanın dönüş değerini.

# atama_donus.rb — atama ifadesi bir deger tasir
puts defined?(a = 1)
puts defined?(puts)
puts defined?(1 + 2)

deger = (b = 7)
puts deger
puts b
assignment
method
method
7
7

Üç ifade üç farklı tür verdi: atama "assignment", puts çağrısı "method", toplama "method". Yani 1 + 2 ile puts aynı kategoridedir, a = 1 ayrı. Atama yine de bir değer taşır: (b = 7) ifadesinin değeri 7’dir ve ad olarak b bağlanmıştır. Bu iki gözlem birlikte atamanın bir ifade olduğunu ama bir yöntem çağrısı olmadığını söyler.

Kapsam Sınırları ve Paralel Atama

Bu bölüm adların nerede görüldüğünü ölçer. defined? ifadeyi çalıştırmadan yalnız türünü bildirir; burada bir kapsam sondası olarak kullanılıyor. Eksen sözdizimidir: bir adın görünüp görünmemesini dilin kapsam kuralları belirler, bir sınıfın tanımladığı yöntem belirlemez. Üç soru soruluyor: def ile açılan yöntem dıştaki yerel değişkeni görür mü, blok görür mü, ve sınıf gövdesinde açılan @@ işaretli ad hangi türdür?

# kapsam_olcumu.rb — yontem ve blok dis yereli nasil gorur
dis = 10
def yontem_icinde
  defined?(dis).inspect
end
puts yontem_icinde
blok_sonucu = [1].map { defined?(dis) }
puts blok_sonucu.first
class Kayit
  @@adet = 0
  def self.sayac_turu
    defined?(@@adet)
  end
end
puts Kayit.sayac_turu
nil
local-variable
class variable

Üç ölçüm üç ayrı sonuç verdi. def ile açılan yöntemin içinden dıştaki dis yerel değişkeni sorulduğunda defined?(dis).inspect nil basıldı: yöntem gövdesi yeni bir kapsam başlatır ve dıştaki yereller içeri geçmez. Aynı ad bir blok içinden sorulduğunda "local-variable" döndü: blok, tanımlandığı yerin yerellerini görür. @@adet için sonuç "class variable" oldu; sınıf gövdesinde açılan bu tür, @ ve $ işaretlerinden ayrı bir kategoridir. Bu üç sonucun ortak noktası, hiçbirini bir ileti karşılamamasıdır: yöntemin dışarıyı görmemesi, bloğun görmesi ve @@ işaretinin türü, hepsi dilin yazım kurallarındandır. Adın nerede tanımlandığı kadar, nerede sorulduğu da sonucu değiştirir: aynı dis adı iki farklı yerden sorulunca iki farklı cevap verdi.

# paralel_atama.rb — a, b = b, a bir atamadir
a = 1
b = 2
a, b = b, a
puts a
puts b
puts defined?(a = 3)
2
1
assignment

Paralel atama a, b = b, a iki adın değerlerini değiş tokuş etti: a artık 2, b ise 1. Geçici üçüncü bir değişken gerekmedi, çünkü sağ taraf, sol taraftaki adlar yeniden bağlanmadan önce değerlendirildi. defined?(a = 3) ifadesi "assignment" bildirdi ve bu, önceki bölümün bulgusuyla uyumludur: atama bir ileti değil, dilin yazımıdır. defined? burada tek bir atamaya soruldu, paralel atamanın kendisine sorulmadı; değiş tokuşun bir yöntem çağrısıyla yapıldığını gösteren bir iz de çıktıda yoktur. İki değişkenin yerini değiştirmek, Ruby’de tek satırlık bir işlemdir; bu, adların nesneleri gösteren etiketler olduğunu, değerin ise etiketin arkasındaki nesne olduğunu bir kez daha gösterir.

DT8. Sınıf değişkeni (@@) yalnız defined? ile yoklandı. Sınıf değişkeninin kalıtımda nasıl paylaşıldığı bu kursun konusu değildir ve ölçülmedi.

Atama Zinciri ve Dağıtma

Atamanın dilin yazımı olduğu ölçüldü. Bu bölüm atamanın üç ek biçimini yoklar: zincirleme atama, artan hedefe dağıtma ve eksik sağ taraf. Her biri aynı soruyu sorar: adlar nesneyi mi gösterir, değeri mi kopyalar? Eksen sözdizimidir; yalnız bir satırda << iletisinin sahibi de basılır, çünkü zincirleme atamadan sonra nesneyi değiştiren şey bir iletidir.

# atama_zinciri.rb — zincirleme atama ve dagitma
x = y = [1]
puts x.equal?(y)
y << 2
puts x.inspect
puts x.method(:<<).owner
bas, *kalan = 10, 20, 30
puts bas
puts kalan.inspect
ilk, = [7, 8]
puts ilk
p1, p2 = 1
puts p2.inspect
puts defined?(Math)
puts defined?(puts)
puts defined?(String.new)
true
[1, 2]
Array
10
[20, 30]
7
nil
constant
method
method

x = y = [1] yazımı iki adı da aynı nesneye bağladı: x.equal?(y) true verdi. Bu yüzden y << 2 ile dizi değişince x de [1, 2] gösterdi. Burada değişikliği yapan şey atama değil, << iletisidir ve onun sahibi Array çıktı. Yani atama nesneyi kopyalamadı, yalnız ad bağladı; nesneyi değiştiren bir ileti geldiğinde ise aynı nesneye bağlı bütün adlar değişikliği gördü. Bu, önceki bölümlerde kurulan sezginin sınanmasıdır: ad bir etikettir, değer ise etiketin arkasındaki nesnedir. Etiketin başka bir etikete atanması nesneyi çoğaltmaz.

Dağıtma biçimleri de dilin yazımıdır. bas, *kalan = 10, 20, 30 ilk değeri bas’a, kalanları bir diziye verdi: bas 10, kalan [20, 30] oldu. ilk, = [7, 8] yazımındaki sondaki virgül, hedeflerin yalnız ilkini aldığını belirtti ve ilk 7 oldu. Sağ taraf eksik kaldığında hata verilmedi: p1, p2 = 1 satırında p2 nil oldu. Son üç satır defined? sondasının farklı ifade türlerine verdiği cevaptır: Math için constant, puts için ve String.new için method çıktı. Sabitin constant, çağrının ise method olarak sınıflanması, defined? sondasının ifadenin neye benzediğine baktığını gösterir.

DT9. defined?(Math) sonucunun constant olması bu sürümde ölçüldü. Aynı sabit için başka sürümlerde farklı bir sözcük dönebilir; derste yazılan sözcük dilin sözü değil bu sürümün çıktısıdır.

Tanım Sorgusu ve Üst Düzey Nesne

defined? yalnız yerel değişkenlere değil, her ad türüne bakar. Bu bölüm sondayı genel değişken, örnek değişkeni ve birkaç ifade üzerinde dener ve üst düzeydeki self nesnesinin ne olduğunu sorar. Eksen sözdizimidir: defined? bir yöntem değil dilin kendi işleci olduğu için sonuç, ifadenin neye benzediğiyle ilgilidir.

# tanim_sorgusu.rb — her ad turu icin defined? cevabi
d1 = defined?($genel)
puts d1.inspect
$genel = 1
puts defined?($genel)
d2 = defined?(@ornek)
puts d2.inspect
@ornek = 2
puts defined?(@ornek)
puts defined?(self)
puts defined?(nil)
puts defined?(String)
puts defined?(3 + 4)
puts self.class
puts self.inspect
nil
global-variable
nil
instance-variable
self
nil
constant
method
Object
main

Genel değişken $genel atanmadan önce nil, atandıktan sonra "global-variable" verdi. Örnek değişkeni @ornek de aynı sırayı izledi: önce nil, sonra "instance-variable". Yani tanımlı olmak, adın değil atamanın bir sonucudur: ad ancak bir değer bağlandığında tanımlı sayılır. defined?(self) "self" verdi, defined?(nil) ise "nil", defined?(String) ise "constant" oldu. defined?(3 + 4) "method" bildirdi: sondayı ilgilendiren, ifadenin en dıştaki işleminin bir ileti olmasıdır.

Son iki satır, bütün bu dosyanın içinde çalıştığı nesneyi sordu. self.class Object, self.inspect ise main verdi. Yani bir .rb dosyasında alıcısız yazılan her ifade, sınıfı Object olan ve adı main basılan bir nesnenin içinde çalışır. Üst düzeyde atanan @ornek değişkeni de bu nesneye bağlıdır. Alıcısız yazılan bir çağrının, örneğin önceki bölümde defined? ile yoklanan puts’un örtük alıcısı da bu main nesnesidir. Sözdizimi kapsamı, adların nerede sorulduğunu belirler; self ise sorunun hangi nesnede yanıtlandığını. Bu bölümün sondaları aynı noktaya varır: defined? adın tanımlı olup olmadığını ve türünü söyler, ama değerini söylemez. Tanımlı olmayan ad nil verir, tanımlı olan ad kendi türünü bildirir. Bu iki cevap, bir adı kullanmadan önce yoklamak için yeterlidir. 3 + 4 için method cevabı gelse de cevabı veren yine defined? işlecidir: ifade çalıştırılmadı, yalnız biçimine bakıldı.

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

Bu dersin yanıtlayanı sözdizimidir: = işaretinin ad bağlama davranışı dilin kendi yazımından gelir, bir sınıfın tanımladığı yöntemden gelmez. Burada kayma += ile olur. y += 1 yazıldığında içinde bir toplama olduğu için bunun bir ileti olduğu düşünülebilir. Ölçüm şunu soruyor: y += 1, y ||= 3 ve y + 1 aynı kategoride mi?

# birlesik_atama.rb — += atamadir, icindeki toplama ise iletidir
y = 1

puts defined?(y += 1)
puts defined?(y ||= 3)
puts defined?(y + 1)
puts y
puts y.method(:+).owner
assignment
assignment
method
1
Integer

defined?(y += 1) ve defined?(y ||= 3) ikisi de "assignment" verdi, y + 1 ise "method". Ardından puts y hâlâ 1 bastı: defined? atamayı çalıştırmadı. Toplamanın yanıtlayanı ise önceki derste olduğu gibi Integer’dır. Aynı satırda iki iş görülür: adı bağlamak dilin işidir, yani sözdizimi; iki sayıyı toplamak sınıfın işidir. Bu dersin yanıtlayanı olarak yalnız birincisi sayılır, çünkü ders ad bağlama üzerine kuruludur.

Üç ölçümün ortak sonucu şudur: Ruby’de bir ad yazıldığında önce adın işaretine bakılır, sonra işaretin bildirdiği yere. Aynı x = 5 ifadesi, başına @ ya da $ konunca başka bir yere yazılır, ama üçünde de = işaretini çalıştıran şey dilin kendisidir. Bu yüzden sonraki derslerde atamayı ayrıca ölçmeye gerek kalmayacak; bir ifadede = gördüğümüzde ileti aramayız.

Özet

  • Adın başındaki işaret, adın türünü belirler: işaretsiz küçük harf yerel değişken, @ örnek değişkeni, $ genel değişken, büyük harf sabittir.
  • defined? ifadeyi çalıştırmadan türünü bildirir: tanımsız sabit için nil, atama için "assignment" döner.
  • Sabit adın bağlanmasını işaretler ama nesneyi dondurmaz; sabite bağlı bir dizi değişebilir.
  • Atama bir yöntem çağrısı değildir: defined? a = 1 için "assignment", 1 + 2 için "method" verir.
  • Bu dersin yanıtlayanı sözdizimidir: y += 1 ve y ||= 3 atamadır, içindeki toplamayı ise Integer yanıtlar.
  • defined? kapsamı da ölçer: yöntem gövdesi dıştaki yereli görmez (nil), blok görür ("local-variable"); a, b = b, a yerleri değiştirir, x = y = [1] iki adı aynı nesneye bağlar.

Sonraki Adım

Ad bağlama dilin işi olarak ölçüldü. Dilin kendi yazımıyla çalışan başka yapılar da var: if, while ve case. Bunlardan case’in dallarını kim seçiyor, bir when satırı nesneye bir ileti mi gönderiyor? Sonraki ders, Denetim Akışı, bu soruyu ö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