---
title: 'Ruby Temelleri'
source: 'https://academia.sh/tr/kurslar/ruby-temelleri'
language: tr
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Her değerin nesne olduğu modelde bir işlemin hangi iletiye karşılık geldiğini açıklar'
    - 'Bir iletiyi sınıfın, modülün, bloğun ya da sözdiziminin yanıtladığını ölçerek ayırt eder'
    - 'Değişkenler, denetim akışı, yöntemler, semboller ve dizgilerle temel program yazar'
    - 'Dizi ve karma yapıları bloklar, Enumerable yöntemleri, proc ve lambda ile işler'
    - 'İstisnaları yakalar, yeniden dener, kaynakları temizler ve dosyalarla çalışır'
---

# Ruby Temelleri

Ruby'de 5 + 3, x = 5, bir dizinin map'i ve 5.between?(1, 10) aynı biçimde yazılır, ama sonucu üreten kod dört ayrı yerde durur: alıcının sınıfında, sınıfa karıştırılmış bir modülde, çağıranın verdiği blokta ya da dilin kendi sözdiziminde. Kurs her düzeneği tek bir soruyla ölçer — bu iletiyi kim yanıtlıyor? Nesne modeli, değişkenler, denetim akışı, yöntemler, semboller, koleksiyonlar, bloklar, Enumerable, proc ve lambda, istisnalar ve dosya işlemleri aynı çerçeveden geçirilir.

## Dil Temelleri

Okunabilirliği önceleyen tasarımın işleçleri bile yönteme çevirmesi, ilkel değerlerin de nesne olması, ad kuralları ve kapsam işaretleri, değer üreten koşullar ve döngüler, örtük dönüşlü yöntemler ve semboller ile dizgilerin iki ayrı temsil olması.

### Ruby'nin Tasarım Felsefesi

Source: https://academia.sh/tr/kurslar/ruby-temelleri/rubynin-tasarim-felsefesi

Ruby'de 5 + 3, x = 5, bir dizinin map'i ve 5.between?(1, 10) aynı biçimde yazılır; sonucu üreten kod ise dört ayrı yerde durur. Ders bu dört yanıtlayanı on iki gözlemle ölçer ve okunabilirliği önceleyen tasarımın işleçleri bile yönteme çevirdiğini gösterir.

M12 PHP ile kapandı. Orada bir sorunun hangi kanaldan duyulduğu, bir değişikliğin nereye ulaştığı ve bir parçanın bir istekte koşup koşmadığı ölçüldü. Ruby müfredatı başka bir sorudan başlar. `5 + 3`, `x = 5`, `[1, 2, 3].map { ... }` ve `5.between?(1, 10)` ekranda aynı biçimde yazılır: bir değer, bir nokta ya da işleç, bir ad. Ama bu dört ifadenin sonucunu üreten kod dört ayrı yerde durur. Bu kurs her düzeneği tek bir soruyla ölçer: bu iletiyi kim yanıtlıyor?

İleti (message), bir nesneye gönderilen ad ve argümanlardır; iletiyi alan nesneye alıcı (receiver), iletiye karşılık çalışan koda yöntem (method) denir. Programlama Temelleri kursunda (M01/K02) değişken, tip ve fonksiyon kavramları kurulmuştu; burada yeni olan, bu kavramların Ruby'de hangi düzenekle verildiğidir. Bu ders düzeneğin dilini ve ölçünün çekirdeğini kurar, sonraki beş ders aynı çekirdekten birer kesit alır.

#### Dört Yanıtlayan

Bir iletiyi karşılayan kodun bulunabileceği dört yer vardır. Kurs boyunca bu dört ad değişmez; kategorinin üst adı **yanıtlayan**dır. "Sınıf" sözcüğü Ruby'nin `Class`'ıyla çakıştığı için ölçü kategorisi yazılırken "yanıtlayan sınıf" ya da "sınıf yanıtlayanı" denir; çıplak "sınıf" yalnız Ruby sınıfıdır.

| Yanıtlayan | Tanım |
|---|---|
| sınıf | İletiyi karşılayan yöntem, alıcının ait olduğu bir sınıfta tanımlıdır |
| modül | Yöntem bir modülde (module) tanımlıdır ve sınıfa karıştırılmıştır |
| blok | Yöntem çağıranın verdiği bloğa dayanır; aynı ileti başka blokla başka sonuç verir |
| sözdizimi | Ortada ileti yoktur; ifadeyi dilin kendisi işler |

Bu ayrım önceki müfredatlardaki bir soruya benzer görünür ama aynı değildir. M08/K03'te bir davranışın kimin sözü olduğu soruldu: dil mi, kitaplık mı, programcı mı vaat ediyor. Burada söz aranmaz, kod aranır; ölçülen, davranışı vaat eden taraf değil, iletiyi o anda karşılayan tanımdır.

**DT1.** Bu derste "yanıtlayan", iletiyi karşılayan yöntemin tanımının bulunduğu yerdir. Yöntemin hangi sırayla aranıp bulunduğu bu kursta ölçülmez; yalnız sonuç, yani sahip ölçülür. Arama sırası müfredatın bir sonraki kursunun konusudur.

#### Okunabilirlik Önceliği

Ruby'nin tasarım tercihi, kodun bir cümle gibi okunmasını işleç ekonomisinin önüne koymaktır. Bu tercih en açık biçimde adlandırılmış yöntemlerde görülür: `5.between?(1, 10)` ifadesi okunduğu gibi bir sorudur. İkinci sonuç daha az görünürdür: işleçler de yöntemdir. Aşağıdaki dosya aynı toplamı üç yazımla verir.

```ruby
# isleclar.rb — isleclerin de yontem oldugunu gosterir
puts 1 + 2
puts 1.+(2)
puts 5.send(:+, 3)
puts 5.between?(1, 10)
puts 5.clamp(1, 3)
```

```
3
3
8
true
3
```

`1 + 2` ile `1.+(2)` aynı sonucu verir, çünkü ilki ikincinin kısa yazımıdır: alıcı `1`, ileti `+`, argüman `2`. `send` iletinin adını sembol olarak alır ve `5.send(:+, 3)` aynı iletiyi gönderir. Böylece `5 + 3`, `x.upcase` ve `5.between?(1, 10)` arasında yazım farkı kalsa da düzenek farkı kalmaz: üçü de bir alıcıya gönderilen birer iletidir. Bu, sonraki derslerin dayandığı sezgidir; her işlemin bir ileti olduğu bir dilde "kim yanıtlıyor" sorusu anlamlı olur.

**DT2.** Ölçümler Ruby 4.0.7'de koşturuldu. Bir yöntemin hangi sınıfta ya da modülde tanımlı olduğu sürümden sürüme taşınabilir; derste yazılan sahip adları bu sürümün sonucudur, dilin sözü değildir.

#### Aynı İşin İki Yazımı

Okunabilirlik tercihi yalnız yöntem adlarında değil, aynı işin birden çok yazıma sahip olmasında da görünür. Ruby'de bir işi görmenin tek yolu yoktur ve yazarın seçtiği yazım, okuyana işin amacını ne kadar açık söylediğini belirler. Aşağıdaki iki dosya bu tercihin ölçülebilir kısmını sorar: aynı işin iki yazımı yalnız görünüşte mi ayrılır, yoksa farklı bir yöntemi mi çağırır? Ölçü ekseni yanıtlayandır; her yazımın iletisini kimin karşıladığı, mümkün olan yerde, çıktının yanında basılır.

```ruby
# iki_yazim.rb — ayni isin iki yazimi ve yanitlayanlari
dizi = [4, 5, 6]
sayac = 0
dizi.each { |n| sayac += n }
puts sayac
puts dizi.sum
puts dizi.method(:each).owner
puts dizi.method(:sum).owner
x = 7
puts "kucuk" unless x > 10
puts "kucuk" if !(x > 10)
```

```
15
15
Array
Array
kucuk
kucuk
```

Sayaçlı yazım ile `sum` aynı toplamı, `15`'i verdi. İkisinin yanıtlayanı da `Array` çıktı: `each` de `sum` da dizinin sınıfında tanımlıdır. Yani iki yazım arasındaki fark yanıtlayanda değil, okumadadır. Sayaçlı yazımda bir değişkenin açılması, bir bloğun yazılması ve bloğun içinde değişkenin güncellenmesi birlikte okunur; `sum` yazımında ise niyet tek adla söylenir. `unless` ile olumsuzlamalı `if` ise aynı satırı iki kez bastırdı. Burada yanıtlayan sorusu farklı bir cevap taşır: `unless` ve `if` anahtar sözcüktür, bir yöntem değildir; bu yüzden bu iki satırın yanıtlayanı olarak dilin sözdizimi sayılır. Koşulun içindeki `x > 10` ise bir iletidir; sahibi bu dosyada basılmadı. Olumsuzlamalı yazımdaki olumsuzlama işleci de ayrı bir iletidir ve `unless` yazımında bulunmaz; onun sahibi de burada ölçülmedi. Olumsuz bir koşulu tersine çevirip olumsuzlamalı `if` ile yazmak da çalışır, ancak okuyan iki ayrı işlem, önce karşılaştırmayı, sonra olumsuzlamayı zihninde birleştirmek zorunda kalır.

```ruby
# parantez.rb — parantezli ve parantezsiz cagri ayni iletidir
liste = [3, 1, 2]
a = liste.push 9
b = liste.push(9)
puts a.equal?(b)
puts liste.size
puts liste.method(:push).owner
puts liste.sort.first
puts liste.sort().first()
puts method(:puts).owner
```

```
true
5
Array
1
1
Kernel
```

Parantezsiz `liste.push 9` ile parantezli `liste.push(9)` aynı diziyi döndürdü: `equal?` `true` verdi. İki çağrı da aynı nesneyi geri verdi ve dizi iki kez büyüyerek 5 elemana ulaştı. `push` iletisinin yanıtlayanı `Array`'dir. `sort.first` ile `sort().first()` aynı `1` değerini verdi, ve `puts` çağrısının sahibi `Kernel` çıktı. İki yazım çiftinin hiçbiri ayrı bir düzenek değildir: parantez yalnız argümanların nerede bittiğini işaretler, ne alıcıyı ne de iletiyi değiştirir. Bu ölçümden çıkan kural şudur: Ruby'de okunabilirlik, yazarın elindeki bir serbestliktir ve serbestlik düzeneği değiştirmez. Aynı işi görmek için seçilen yazım, makinenin değil okuyanın işini kolaylaştırmalıdır.

**DT3.** Hangi yazımın daha çabuk okunduğu ölçülmedi. Ölçülen yalnızca, iki yazımın aynı sonucu verdiği ve yanıtlayanın değişmediğidir; okunabilirlik yargısı ekibin yazım tercihine bağlıdır ve bu kursun savı değildir.

#### Okunabilirliğin Sınırı

Okunabilirlik önceliği bir serbestlik tanır, ama her serbestlik iletiyi değiştirmeden yazılamaz. Bu bölümün sorusu şudur: yazımın değiştiği yerde ileti de değişir mi? İki yazım yan yana konunca bazen ileti aynı kalır, bazen de yazım farkı ayrı bir iletiye karşılık gelir. Ölçü yanıtlayan ve sonuçtur; dosya kullanılan dört iletinin sahibini basar.

```ruby
# yazim_sinir.rb — yazim degisince ileti degisir mi
a = 2
b = 3
puts a + b * 2
toplam = (a + b) * 2
puts toplam == 10
puts a.+(b).*(2)
puts a.method(:+).owner
puts a.method(:*).owner
puts 10.fdiv(4)
puts 10 / 4
puts 10.method(:fdiv).owner
puts 10.method(:/).owner
```

```
8
true
10
Integer
Integer
2.5
2
Integer
Integer
```

`a + b * 2` ifadesi `8` verdi; çarpma toplamadan önce yapıldı. `(a + b) * 2` ise `10` verdi ve `toplam == 10` karşılaştırması `true` oldu. Üçüncü satırdaki `a.+(b).*(2)` yazımı da `10` verdi: alıcı `a`, ileti `+`, argüman `b`, ardından sonuca `*` iletisi gönderildi. Yani yazarın parantezle yaptığı öncelik seçimi, noktalı yazımda iletilerin zincirleme sırasıyla birebir aynı sonucu verir. İşleç öncelikleri bu yüzden bir yanıtlayan sorusu değildir; hangi iletinin hangi alıcıya gideceğini belirleyen bir yazım kuralıdır. Sahip satırları iki işlecin de `Integer` sınıfında tanımlı olduğunu gösterdi.

Okunabilirlik açısından ilginç olan, iki bölme yazımıdır. `10.fdiv(4)` `2.5`, `10 / 4` ise `2` verdi. İkisinin de yanıtlayanı `Integer` çıktı, ama sonuçlar farklıdır: `/` tam sayı bölmesi yapar, `fdiv` kesirli sonuç verir. Yazım farkı burada aynı işin iki biçimi değil, iki ayrı iletidir. Ruby'nin tasarımı bu ayrımı adla yapar: kesirli sonuç isteyen kişi `fdiv` yazar ve okuyan niyeti adından anlar. Bu da bölümün sonucudur: yazım serbestliği, adlandırılmış iletilerde güvenle kullanılır; işleçlerde ise sonucun aynı kalıp kalmadığı ölçülerek bilinir.

**DT4.** Tam sayı bölmesinin `Integer` için `2` verdiği bu sürümde ölçüldü. Bölme davranışının başka sayı türlerinde de aynı olduğu ölçülmedi.

#### Çekirdek: Yardımcı ve On İki Gözlem

Kurs iki dosyadan oluşan bir ölçüm çekirdeği kullanır. İlki, bir iletiyi sınıflandıran yardımcıdır ve kural olarak yazılmıştır: etiket değil, soru sırası karar verir. Önce `defined?` ifadenin bir yöntem çağrısı olup olmadığını söyler; değilse yanıtlayan sözdizimidir. Sonra aynı ileti iki farklı blokla iki farklı sonuç veriyorsa yanıtlayan bloktur. Son olarak yöntemin sahibi bir `Class` örneğiyse sınıf, değilse modüldür.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

Bu dosyanın çıktı bloğu yoktur: tek başına bir şey yazdırmaz, yalnız diğer dosyanın çekeceği bir yardımcıdır. İkinci dosya on iki gözlemi yapar. Her gözlem bir lambda içindedir; lambda çağrılınca `yanitlayan` yardımcısına iletinin `defined?` sonucunu, yöntemin sahibini ve sonucunu verir. Dosya çalışma dizinine bir `not.txt` yazar, çünkü son gözlem onu okur.

```ruby
# yanitlayan.rb — on iki gozlem: bu iletiyi kim yanitliyor
require_relative "yanit"

File.write("not.txt", "ilk satir\nikinci satir\n")
sayilar = [1, 2, 3]

gozlemler = {
  "okunur karsilastirma" => -> { yanitlayan(defined?(5.between?(1, 10)), sahip: 5.method(:between?).owner, sonuc: 5.between?(1, 10)) },
  "tam sayi toplami"     => -> { yanitlayan(defined?(5 + 3), sahip: 5.method(:+).owner, sonuc: 5 + 3) },
  "yerel atama"          => -> { yanitlayan(defined?(x = 5)) },
  "aralik eslesmesi"     => -> { yanitlayan(defined?((1..10) === 7), sahip: (1..10).method(:===).owner, sonuc: (1..10) === 7) },
  "alicisiz cagri"       => -> { yanitlayan(defined?(format("%03d", 7)), sahip: method(:format).owner, sonuc: format("%03d", 7)) },
  "sembole donusum"      => -> { yanitlayan(defined?("ad".to_sym), sahip: "ad".method(:to_sym).owner, sonuc: "ad".to_sym) },
  "varsayilan deger"     => -> { yanitlayan(defined?(Hash.new), bloklu: -> { [Hash.new { 0 }[:x], Hash.new { |_, k| k.to_s }[:x]] }) },
  "eleman donusumu"      => -> { yanitlayan(defined?(sayilar.map), bloklu: -> { [sayilar.map { |n| n * 2 }, sayilar.map { |n| n.to_s }] }) },
  "dilimleme"            => -> { yanitlayan(defined?((1..5).each_slice(2)), sahip: (1..5).method(:each_slice).owner, sonuc: (1..5).each_slice(2).to_a) },
  "lambda cagrisi"       => -> { yanitlayan(defined?(->(n) { n * 2 }.call(3)), sahip: ->(n) { n * 2 }.method(:call).owner, sonuc: ->(n) { n * 2 }.call(3)) },
  "kurtarma"             => -> { yanitlayan(defined?(begin; Integer("x"); rescue ArgumentError; 0; end)) },
  "dosya okuma"          => -> { yanitlayan(defined?(File.open("not.txt")), bloklu: -> { [File.open("not.txt") { |f| f.gets.chomp }, File.open("not.txt") { |f| f.readlines.size }] }) },
}

adet = { "sinif" => 0, "modul" => 0, "blok" => 0, "sozdizimi" => 0 }

printf("%-22s%-11s%s\n", "gozlem", "yanitlayan", "kanit")
gozlemler.each do |ad, gozlem|
  tur, kanit = gozlem.call
  adet[tur] += 1
  printf("%-22s%-11s%s\n", ad, tur, kanit)
end

puts
adet.each { |tur, n| printf("%-11s%d\n", tur, n) }
puts "toplam gozlem: #{adet.values.sum}"
```

```
gozlem                yanitlayan kanit
okunur karsilastirma  modul      Comparable, sonuc true
tam sayi toplami      sinif      Integer, sonuc 8
yerel atama           sozdizimi  defined? assignment
aralik eslesmesi      sinif      Range, sonuc true
alicisiz cagri        modul      Kernel, sonuc "007"
sembole donusum       sinif      String, sonuc :ad
varsayilan deger      blok       0 / "x"
eleman donusumu       blok       [2, 4, 6] / ["1", "2", "3"]
dilimleme             modul      Enumerable, sonuc [[1, 2], [3, 4], [5]]
lambda cagrisi        sinif      Proc, sonuc 6
kurtarma              sozdizimi  defined? expression
dosya okuma           blok       "ilk satir" / 2

sinif      4
modul      3
blok       3
sozdizimi  2
toplam gozlem: 12
```

On iki gözlemin 4'ünü bir sınıf, 3'ünü bir modül, 3'ünü bir blok yanıtladı; 2'sinde ileti yoktu, ifadeyi sözdizimi işledi. Bu sayılar kursun bütün dersleri için bağlayıcıdır: sonraki dersler bu sayıları değiştirmez, her biri tablodan kendi satırını alıp ayrıntısında ölçer.

Tablodan dört okuma çıkar. Sınıf yanıtlayanı en dolaysız olanıdır: `5 + 3`'teki `+` bir işleç gibi görünür ama `Integer` sınıfında tanımlı bir yöntemdir; `"ad".to_sym` `String`'de, lambda'nın `call`'ı `Proc`'ta tanımlıdır. Modül yanıtlayanı sınıfın kendisinde bulunmayan bir yeteneği gösterir: `between?` `Integer`'da değil `Comparable`'dadır, `each_slice` `Range`'te değil `Enumerable`'dadır, alıcısız `format` `Kernel`'dedir. Blok yanıtlayanı sonucu çağırana bırakır: `Hash.new`, `map` ve `File.open` aynı iletiyle iki sonuç verdi ve fark yalnız verilen bloktaydı. Sözdizimi yanıtlayanında alıcı da ileti de yoktur: `x = 5` için `defined?` `"assignment"`, `begin ... rescue ... end` için `"expression"` döndürdü.

Çıktıda bilerek basılmayan şeyler de vardır: `object_id`, bellek adresi taşıyan `inspect` çıktısı ve istisna ileti metni. Bu değerler her koşumda değişir ya da ortama bağlıdır; yerlerine sınıf adı, mantıksal sonuç ve sayı basılır.

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

Bu dersin satırı `okunur karsilastirma`'dır ve yanıtlayanı modüldür: `5.between?(1, 10)` iletisini `Comparable` karşılar. Ancak bu cümle yarım kalırsa yanıltır. Modülün tanımladığı yöntem, karşılaştırmanın kendisini yapmaz; onu alıcıya sorar. `between?` bir değerin iki sınır arasında olup olmadığını söylemek için `<=>` iletisini alıcıya gönderir ve karşılaştırmanın nasıl yapılacağı alıcının sınıfındaki `<=>` yöntemindedir. Sahipleri yan yana basalım.

```ruby
# karsilastirma.rb — between? Comparable'da, <=> ise alicinin sinifinda
[5, "a", :a].each do |alici|
  printf("%-4s between? %-11s <=> %s\n", alici.inspect, alici.method(:between?).owner, alici.method(:<=>).owner)
end
```

```
5    between? Comparable  <=> Integer
"a"  between? Comparable  <=> String
:a   between? Comparable  <=> Symbol
```

Üç alıcıda `between?`'in sahibi aynıdır, `<=>`'in sahibi her seferinde alıcının kendi sınıfıdır. Aynı modül üç ayrı sınıfa aynı yeteneği verir, çünkü üçü de karşılaştırmayı kendisi yanıtlar. Modül bu yüzden tek başına yanıt vermez: yeteneği modülden gelir, dayanağı sınıftan.

Bu sınır, yanıtlayanı tek bir sahip adına indirgemeyi engeller. Dört yanıtlayanlı sınıflandırma iletiyi karşılayan tanımı bulur; o tanımın başka bir iletiye dayanması, yeni bir ölçüm sorusudur ve çoğu zaman cevabı bir sınıftır.

#### Özet

- Ruby'de her işlem bir alıcıya gönderilen iletidir; `1 + 2` ile `1.+(2)` aynı iletidir ve `send` iletiyi adıyla gönderir.
- Ölçü sorusu "bu iletiyi kim yanıtlıyor"dur ve dört yanıtlayanı vardır: sınıf, modül, blok, sözdizimi.
- Yanıtlayanı etiket değil kural verir: önce `defined?`, sonra iki blokla karşılaştırma, sonra yöntemin sahibi.
- Çekirdeğin on iki gözleminde dağılım 4 sınıf, 3 modül, 3 blok ve 2 sözdizimidir.
- Bu dersin yanıtlayanı modüldür: `between?` `Comparable`'dadır, ama karşılaştırmanın kendisi olan `<=>` alıcının sınıfındadır.
- Aynı işin iki yazımı (`each` ile sayaç ve `sum`, parantezli ve parantezsiz çağrı) ölçülen sahipleri değiştirmedi; `unless` ile olumsuzlamalı `if` aynı sonucu verdi. Okunabilirlik yazarın seçimidir, düzeneği değiştirmez.

#### Sonraki Adım

`5 + 3`'ün `+`'sının `Integer`'da tanımlı olması bir şeyi gizli varsayar: `5` bir nesnedir ve bir sınıfa aittir. İlkel değerlerin de nesne olup olmadığı ve alıcı ile yanıtlayanın birbirinden nasıl ayrıldığı sonraki dersin, Nesne Modeli Sezgisi'nin konusudur.

### Nesne Modeli Sezgisi

Source: https://academia.sh/tr/kurslar/ruby-temelleri/nesne-modeli-sezgisi

Sayıların, nil'in ve true'nun da nesne olması; 5 + 3'te alıcının 5, yanıtlayanın Integer olması; sınıfların kendilerinin de nesne olduğu ve kendi iletilerini başka bir sınıfın yanıtladığı.

Önceki ders her işlemin bir alıcıya gönderilen ileti olduğunu kurdu ve `5 + 3`'ün `+`'sının `Integer` sınıfında tanımlı olduğunu on iki gözlemin içinde gösterdi. Bu cümle bir şeyi varsayıyordu: `5` bir nesnedir ve bir sınıfa aittir. Bu ders o varsayımı ölçer. JavaScript Temelleri kursunda (M06/K01) ilkel tipler ölçülmüştü; Ruby'de aynı soru başka biçimde sorulur: bir değere ileti gönderilebiliyorsa, o iletiyi kim karşılıyor?

Nesne yönelimi M08'de Java ile kurulmuştu ve nesne ile sınıf kavramları orada tanımlandı. Burada yeni olan, Ruby'de sayıların, `nil`'in ve doğruluk değerlerinin de aynı modelin içinde olmasıdır. Ayrıca bu derste çekirdekten tek bir satır alınır: `tam sayi toplami`.

#### Her Değer Bir Nesnedir

Ruby'de ilkel diye ayrı bir değer sınıfı yoktur. Aşağıdaki dosya sekiz farklı değere aynı iletiyi gönderir: `class`. Her satırda değerin gösterimi ve sınıfı yan yana basılır.

```ruby
# degerler.rb — her degerin bir sinifi vardir
degerler = [5, 3.5, "ad", :ad, nil, true, [1], 1..2]

degerler.each do |deger|
  printf("%-6s %s\n", deger.inspect, deger.class)
end
```

```
5      Integer
3.5    Float
"ad"   String
:ad    Symbol
nil    NilClass
true   TrueClass
[1]    Array
1..2   Range
```

Sayının, ondalık sayının, dizginin, sembolün, `nil`'in, doğruluk değerinin, dizinin ve aralığın hepsi bir sınıftan gelir. `nil` ve `true` bir sınıfın tek örneğidir: `NilClass` ve `TrueClass`. Bu, değerlere ileti gönderilebileceği anlamına gelir. Aşağıdaki dosya sayıya, `true`'ya ve `nil`'e birer ileti gönderir ve her yöntemin sahibini sorar.

```ruby
# ilkel_iletiler.rb — sayi, true ve nil iletileri yanitlar
puts 5.times.class
puts 5.method(:times).owner
puts true.method(:&).owner
puts nil.method(:to_a).owner
p nil.to_a
p true & false
```

```
Enumerator
Integer
TrueClass
NilClass
[]
false
```

`5.times` bir yineleyici döndürür ve sahibi `Integer`'dır. `true & false` bir işleç gibi görünür ama `TrueClass`'ta tanımlı bir yöntemdir. `nil.to_a` boş diziyi verir ve sahibi `NilClass`'tır. Üç iletinin üçü de alıcının kendi sınıfında yanıt buldu.

**DT5.** Bu derste "nesne", üzerine ileti gönderilebilen her değerdir. Bir değerin bellekte nasıl tutulduğu ölçülmez; yalnız davranış, yani hangi iletiye kimin yanıt verdiği ölçülür. `object_id` ve bellek adresi taşıyan `inspect` çıktıları bu yüzden basılmaz.

#### Alıcı ve Yanıtlayan

`5 + 3` ifadesinde alıcı `5`'tir, ileti `+`'dır, argüman `3`'tür. M11/K04'te işlemin hedef nesnesinin hangisi olduğu ölçülmüştü. Burada alıcı bellidir; soru, alıcıya gelen iletiyi hangi tanımın yanıtladığıdır. `5 + 3`'te alıcı `5`'tir, yanıtlayan `Integer`'dır.

```ruby
# alici_yanitlayan.rb — 5 + 3'te alici ile yanitlayan ayri seylerdir
alici = 5

puts defined?(alici + 3)
puts alici.method(:+).owner
puts alici.method(:+).owner.instance_of?(Class)
puts alici + 3
```

```
method
Integer
true
8
```

`defined?` ifade için `"method"` döndürdü: ortada gerçekten bir ileti var. Yöntemin sahibi `Integer`'dır ve `Integer` bir `Class` örneğidir; kursun sınıflandırma kuralı bu durumda yanıtlayanı sınıf sayar. Alıcı bir değerdir, yanıtlayan bir tanımdır. İkisi çoğu zaman aynı adı taşımaz: `5` bir değerdir, `Integer` onun ait olduğu sınıftır.

Bu satır, kursun ortak çekirdeğindeki `tam sayi toplami` gözlemidir ve çekirdeğin tamamında olduğu gibi yanıtlayanı sınıftır. Bu derste `yanit.rb` çekilmez; `defined?` sonucu ve yöntemin sahibi doğrudan basılır. Kural aynıdır: önce `defined?`, sonra sahip.

#### Sınıflar da Nesnedir

Her değerin bir sınıfı olduğuna göre `Integer`'ın da bir sınıfı olmalıdır. `Integer` Ruby'de bir sabittir ve bir nesneyi gösterir; o nesne de bir iletiyi yanıtlayabilir. Peki `Integer`'a bir ileti gönderildiğinde kim yanıtlar? Aşağıdaki dosya `5`'in ve `Integer`'ın sınıfını, ardından `Integer`'a gönderilen `===` iletisinin sahibini basar.

```ruby
# siniflar_nesne.rb — siniflar da nesnedir, kendi iletilerini baska bir sinif yanitlar
puts 5.class
puts Integer.class
puts Integer.method(:===).owner
puts Integer.method(:===).owner.instance_of?(Class)
puts Integer === 5
puts Integer === "5"
```

```
Integer
Class
Module
true
true
false
```

`5`'in sınıfı `Integer`'dır; `Integer`'ın sınıfı `Class`'tır. Yani `Integer` kendi başına bir nesnedir ve ona gönderilen `===` iletisini `Integer` değil `Module` yanıtlar. `Module` da bir sınıftır, bu yüzden kural yanıtlayanı yine sınıf sayar. `Integer === 5` doğru, `Integer === "5"` yanlış döner: `===` burada "bu değer bu sınıfa ait mi" sorusunu sorar.

#### İleti Alıcının Sınıfına Bağlıdır

Her değerin bir sınıfı olması, her değerin her iletiyi yanıtlayamayacağı anlamına gelir. `times` iletisi `Integer`'da tanımlıdır; `nil`'in sınıfı `NilClass`'ta ise yoktur. Aşağıdaki dosya üç iletiyi iki alıcıya sorar ve `nil`'e `times` gönderildiğinde ne olduğuna bakar.

```ruby
# ileti_yoklama.rb — ileti alicinin sinifina baglidir
[5, nil].each do |alici|
  printf("%-4s times: %-5s to_a: %-5s to_s: %s\n", alici.inspect, alici.respond_to?(:times), alici.respond_to?(:to_a), alici.respond_to?(:to_s))
end

begin
  nil.times
rescue NoMethodError => e
  puts e.class
end
```

```
5    times: true  to_a: false to_s: true
nil  times: false to_a: true  to_s: true
NoMethodError
```

`respond_to?` bir nesnenin bir iletiyi yanıtlayıp yanıtlamadığını sorar. `5` `times` ve `to_s` iletilerini yanıtlar; `nil` ise `to_a` ve `to_s` iletilerini yanıtlar, `times` iletisini yanıtlamaz. Yanıtlanmayan iletiyi göndermek `NoMethodError` fırlatır ve burada yalnız istisnanın sınıfı basıldı. Yani "her şey nesnedir" cümlesi "her nesne her iletiyi alır" anlamına gelmez: iletinin karşılığı alıcının sınıfında ya da o sınıfın üst tanımlarında bulunmalıdır.

#### Her Şeyin Nesne Olduğunu Yoklamak

Önceki bölümler nesne modelini alıcı, yanıtlayan ve sınıf üzerinden kurdu. Bu bölüm "her şey nesnedir" savını sayılar, `nil` ve sınıfların kendisi üzerinde tek tek sınar. Savın sınırı da ölçülür: bir değerin hangi türden olduğunu soran iki sorgu, `instance_of?` ve `is_a?`, aynı soruyu sormaz. Eksen yine yanıtlayandır; sorgulardan birinin sahibi çıktının son satırında okunur.

```ruby
# her_sey_nesne.rb — nesne olmayan deger yoktur
puts 5.respond_to?(:+)
puts nil.respond_to?(:to_a)
puts 5.frozen?
puts 5.methods.size > 50
puts Integer.class
puts Class.class
puts 5.instance_of?(Integer)
puts 5.is_a?(Comparable)
puts 5.instance_of?(Comparable)
puts 5.method(:frozen?).owner
```

```
true
true
true
true
Class
Class
true
true
false
Kernel
```

İlk iki satır `true` verdi: `5` toplama iletisini karşılar, `nil` ise `to_a` iletisini karşılar. `5.frozen?` de `true` döndü; sayı değişmez bir nesnedir. `5.methods.size > 50` sorusu `true` oldu. Burada sayının kendisini bilerek basmadık: bir nesnenin kaç yöntemi olduğu sürüme göre değişir, ancak "epeyce yöntemi olduğu" sonucu değişmez. Beşinci ve altıncı satırlar bölümün asıl bulgusudur: `Integer.class` ve `Class.class` ikisi de `Class` bastı. `Integer` bir sınıftır ve bir nesnedir; o nesnenin sınıfı `Class`'tır. `Class`'ın kendisi de bir nesnedir ve onun da sınıfı `Class`'tır. Zincir kendi üzerine kapanır, yani "sınıfın sınıfı nedir" sorusu sonsuza gitmez.

Son üç sorgu iki yöntemin farkını gösterir. `5.instance_of?(Integer)` `true`, `5.is_a?(Comparable)` `true`, `5.instance_of?(Comparable)` ise `false` verdi. `instance_of?` yalnız nesnenin doğrudan sınıfını sorar; `is_a?` ise nesnenin o türden sayılıp sayılmadığını, yani daha geniş bir soruyu sorar. Zincirin nasıl işlediğini burada açmıyoruz; yalnız farkı kaydediyoruz. Son satırdaki sahip `Kernel` çıktı: `5.frozen?` iletisini sayının sınıfı değil, her nesnenin ortak aldığı bir modül karşılar.

**DT6.** `> 50` eşiği bir seçimdir: ölçüm yalnızca sayının eşikten büyük olduğunu gösterir ve sayının kendisi dilin sözü değildir.

```ruby
# nil_nesne.rb — nil de bir nesnedir ve sahibi vardir
puts nil.class
puts nil.to_a.inspect
puts nil.to_s.empty?
puts nil.method(:to_a).owner
puts true.class
puts true.method(:&).owner
```

```
NilClass
[]
true
NilClass
TrueClass
TrueClass
```

`nil` de bir nesnedir: `nil.class` `NilClass` verdi. `nil.to_a` boş bir dizi döndürdü ve `nil.to_s` boş bir dizgi verdiği için `empty?` sorusu `true` oldu. Yani "değer yok" durumu bile iletilere yanıt verir ve bu yüzden `nil` üzerinde `to_a` çağırmak hata değil, `[]` üretir. `nil.method(:to_a).owner` `NilClass` çıktı: yanıtlayan `nil`'in kendi sınıfıdır. `true` için de aynı düzen geçerlidir: sınıfı `TrueClass`, `&` iletisinin sahibi de `TrueClass`. `true` ile `nil`'in sınıfları ayrıdır ve ölçülen iki iletinin sahibi de bu iki sınıfın kendisi çıktı. Bu iki bölümün ortak sonucu, dersin başındaki sezgiyi sıkılaştırır: Ruby'de hiçbir değer iletiye kapalı değildir. Bir ifadenin değeri ne olursa olsun, sonuna bir ileti yazılabilir ve yanıtlayan bir sınıf ya da modül olarak bulunabilir.

#### Zincirleme İletiler ve Dönüş Nesnesi

Her ileti bir nesne döndürür ve döndürülen nesne bir sonraki iletinin alıcısı olur. Bu bölüm, zincirin her halkasında yanıtlayanın değişebildiğini ölçer. Ölçülen ifade `5.to_s.size.to_f` gibi bir zincirdir; zinciri soldan okumak gerekir: önce `5` iletiyi alır, çıkan nesne bir sonrakini alır ve böylece sürer. Eksen yine sahiptir: iki halkanın sahibi ayrı ayrı basılır.

```ruby
# zincir.rb — her ileti bir nesne dondurur ve zincir surer
sonuc = 5.to_s.size.to_f
puts sonuc
puts sonuc.class
puts 5.to_s.class
puts 5.to_s.size.class
puts [3, 1, 2].sort.reverse.first
puts "ruby".reverse.upcase.class
puts 5.method(:to_s).owner
puts 5.to_s.method(:size).owner
```

```
1.0
Float
String
Integer
3
String
Integer
String
```

Zincirin sonucu `1.0` oldu ve sınıfı `Float`'tır. Ara halkaları ayrı ayrı yoklayınca zincirin nasıl kurulduğu görünür: `5.to_s` bir `String` verdi, `5.to_s.size` ise bir `Integer` verdi. Yani aynı ifadenin içinde dört ayrı nesne ve üç ayrı sınıf vardır: `5` için `Integer`, `"5"` için `String`, `1` için yine `Integer` ve sonunda `1.0` için `Float`. `[3, 1, 2].sort.reverse.first` iki dizi halkasından sonra elemana indi ve `3` verdi; `"ruby".reverse.upcase.class` ise `String` bastı, çünkü iki halka da dizgi döndürür.

Sahip satırları, iki farklı halkanın iki farklı sınıfta tanımlı olduğunu gösterdi: `5.method(:to_s).owner` `Integer`, `5.to_s.method(:size).owner` ise `String` çıktı. Aynı `to_s` ve `size` adları başka bir alıcıda başka bir sahibe gidebilirdi; yanıtlayanı alıcının sınıfı belirler, zincirdeki sırası değil. Bu, 'Ruby nesne yönelimlidir' savının somut karşılığıdır: işlem bir nesneye bir ileti göndermektir ve cevap yine bir nesnedir. Bir zincirin okunması için her halkanın dönüş nesnesinin sınıfını bilmek gerekir; sınıf değiştiğinde bir sonraki ileti başka bir yanıtlayana gider. Bu yüzden uzun bir zincirde bir hata oluştuğunda, hatanın nerede çıktığını bulmanın yolu zinciri halka halka bölüp her ara nesnenin sınıfını yoklamaktır.

#### Eşitlik ve Kimlik Ayrı Sorulardır

Eşitlik ile kimlik iki ayrı sorudur. `==` iki nesnenin değerce eşit olup olmadığını, `equal?` ise ikisinin aynı nesne olup olmadığını sorar. Bu bölüm iki sorguyu aynı değerler üzerinde yan yana koyar. Eksen sahiptir: iki sorguyu karşılayan yöntemin tanımlandığı yer sonda basılır.

```ruby
# kimlik.rb — esitlik ile kimlik ayri sorulardir
a = "x"
b = a
puts a.equal?(b)
puts a.equal?("x")
puts a == "x"
puts a.dup.equal?(a)
puts 1.equal?(1)
puts :s.equal?(:s)
puts nil.equal?(nil)
puts 1 == 1.0
puts 1.eql?(1.0)
puts 1.equal?(1.0)
puts a.method(:==).owner
puts a.method(:equal?).owner
```

```
true
false
true
false
true
true
true
true
false
false
String
BasicObject
```

`b = a` atamasından sonra `a.equal?(b)` `true` verdi: iki ad tek nesneyi gösteriyor. `a.equal?("x")` `false`, `a == "x"` ise `true` oldu: metin aynı, nesne farklı. `a.dup.equal?(a)` de `false` verdi, çünkü `dup` yeni bir nesne üretir. Sayı, sembol ve `nil` ise ayrı davrandı: `1.equal?(1)`, `:s.equal?(:s)` ve `nil.equal?(nil)` üçü de `true` verdi; bu değerlerin her yazımı aynı nesnedir.

Sayıların kendi içindeki karşılaştırmada üç sorgu iki farklı cevap verdi. `1 == 1.0` `true` oldu, `1.eql?(1.0)` ile `1.equal?(1.0)` ise `false` verdi. Yani `==` değerlere bakar ve tam sayı ile kesirli sayı arasında eşitlik görür; `eql?` ve `equal?` ise daha katı sorular sorar. Sahip satırları iki sorgunun tanımlandığı yeri gösterdi: `==` için `String`, `equal?` için `BasicObject` çıktı. Yani `==` her sınıfın kendi tanımına bırakılmış bir sorudur, `equal?` ise tek bir ortak yerde tanımlıdır. Bu yüzden bir değerin "aynı" olup olmadığını sormadan önce hangi anlamda sorulduğunu belirtmek gerekir. Sonuç olarak `equal?` kimlik sorusu için, `==` değer sorusu için kullanılır; ikisini karıştırmak, iki ayrı nesneyi aynı sanmanın ya da aynı nesneyi iki sanmanın yolunu açar.

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

Bu dersin ölçümlerinin çoğunda yanıtlayan sınıftır; `class` ve `frozen?` için `Kernel` modülü, `equal?` için `BasicObject` istisnadır. "Sınıf yanıtlayanı" ifadesi ise iki farklı şeye kayabilir. Kayma şöyle olur: `Integer.===` iletisini yanıtlayan `Integer` zannedilebilir. Yukarıdaki koşum bunun yanlış olduğunu gösterdi. Yanıtlayan alıcının kendisi değil, alıcının ait olduğu sınıf zinciridir: alıcı `5` ise `Integer`, alıcı `Integer` ise `Class` zincirinde `Module`.

```ruby
# alicinin_sinifi.rb — yanitlayan alicinin kendisi degil, ait oldugu siniftir
[5, "ad", Integer, String].each do |alici|
  printf("%-8s sinifi %-8s class sahibi %s\n", alici.inspect, alici.class, alici.method(:class).owner)
end
```

```
5        sinifi Integer  class sahibi Kernel
"ad"     sinifi String   class sahibi Kernel
Integer  sinifi Class    class sahibi Kernel
String   sinifi Class    class sahibi Kernel
```

`class` iletisinin sahibi dört alıcıda da `Kernel`'dir; çünkü bu ileti her nesnede ortak bir modülde tanımlıdır. Alıcıların sınıfları ise farklıdır. "Bu iletiyi kim yanıtlıyor" sorusunun iki katmanı vardır: alıcı hangi sınıfa ait ve o sınıf iletiyi kendi mi yanıtlıyor. Bu derste ölçülen birinci katmandır. İkincisinin, yani yöntemin alıcının sınıfında değil bir üst tanımda bulunduğu durumların ölçümü modül yanıtlayanını konu alan Yöntemler dersinde sürer.

#### Özet

- Ruby'de sayılar, `nil`, `true`, dizgiler, semboller, diziler ve aralıklar birer nesnedir; her birinin bir sınıfı vardır.
- `5 + 3`'te alıcı `5`'tir ve yanıtlayan `Integer`'dır; alıcı bir değerdir, yanıtlayan bir tanımdır.
- `5.times`, `true & false` ve `nil.to_a` iletilerini sırasıyla `Integer`, `TrueClass` ve `NilClass` yanıtlar.
- Sınıflar da nesnedir: `Integer.class` `Class`'tır ve `Integer.===` iletisini `Module` yanıtlar.
- Bu dersin yanıtlayanı çoğunlukla sınıftır: iletiyi alıcının kendisi değil, alıcının ait olduğu sınıf karşılar. `class` ve `frozen?` gibi her nesnede ortak iletilerin sahibi ise `Kernel` modülüdür.
- `Integer.class` ve `Class.class` ikisi de `Class` verir; `equal?` kimliği, `==` değeri sorar ve zincirdeki her halkanın yanıtlayanı o halkanın alıcısının sınıfıdır.

#### Sonraki Adım

Nesnelerin bir sınıfı olduğu ve iletinin onlara gönderildiği görüldü. Peki bir ad bir nesneye nasıl bağlanıyor ve `x = 5` yazıldığında kim çalışıyor? Atamanın bir ileti olup olmadığı, ad kuralları ve kapsam işaretleri sonraki dersin, Değişkenler ve Sabitler'in konusudur.

### Değişkenler ve Sabitler

Source: https://academia.sh/tr/kurslar/ruby-temelleri/degiskenler-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.

Ö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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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?

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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?

```ruby
# 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.

### Denetim Akışı

Source: https://academia.sh/tr/kurslar/ruby-temelleri/denetim-akisi

case'in her when dalına === iletisi göndermesi, if ve while'ın ise ileti göndermeyen sözdizimi olması; deyim değiştiriciler ve if'in değer üretmesi; case'in yanıtlayanının dalın nesnesinin sınıfı olması.

Önceki ders atamanın bir ileti değil, dilin kendi yazımı olduğunu ölçtü ve bir soru bıraktı: dilin kendi yazımıyla çalışan `if`, `while` ve `case`'ten hangisi gerçekten ileti gönderiyor? Koşul ve döngü kavramları Programlama Temelleri kursunda (M01/K02) tanıtılmıştı; burada o kavramlar tekrarlanmaz. Bu ders yalnız bir soruyu ölçer: bir `case` dalının seçilmesi sırasında hangi nesneye hangi ileti gidiyor?

Rust kursunda (M10/K01) "bu yapı değer mi üretiyor" sorusu ölçülmüştü. Ruby'de de neredeyse her denetim yapısı değer üretir, ve o soru bu derste yalnız bir yan gözlem olarak geri gelir. Asıl ölçü, dallanmanın arkasındaki iletidir.

#### case Her Dala === Gönderir

Aşağıdaki dosya bir aralık nesnesine `===` iletisi gönderir ve bu iletiyi kimin yanıtladığına bakar. Sonra aynı aralığı bir `case` içinde `when` dalına koyar.

```ruby
# aralik_eslesmesi.rb — case her when dalina === iletisi gonderir
aralik = 1..10

puts aralik.class
puts aralik.method(:===).owner
puts aralik === 7

sonuc = case 7
        when 1..10 then "aralikta"
        else "disinda"
        end
puts sonuc
```

```
Range
Range
true
aralikta
```

`1..10` bir `Range` nesnesidir ve `===` iletisini `Range` yanıtlar. Aralığa `7` sorulduğunda cevap `true` döner. `case 7 when 1..10` yazıldığında dal da eşleşir: çünkü `case` değerini `when` dalının nesnesine `===` iletisinin argümanı yapar. Yani `when 1..10`, perde arkasında `(1..10) === 7` sorusudur.

**DT10.** Bu derste "dal nesnesi", `when` satırında yazılan nesnedir; `case` değeri ise `case` anahtar sözcüğünden sonra gelen nesnedir. `===` iletisinin alıcısı dal nesnesi, argümanı `case` değeridir. Sıra önemlidir: `7 === (1..10)` yazılsaydı alıcı `7` olurdu ve yanıtlayan değişirdi.

#### if ve while İleti Göndermez

`case` dallarında `===` iletisi gittiğini gördük. `if` ve `while` için aynı soru sorulabilir: bir koşul bir iletiyle mi seçiliyor? `defined?` buna doğrudan yanıt verir.

```ruby
# kosul_ifadeleri.rb — if ve while sozdizimidir
puts defined?(if true then 1 end)
puts defined?(while false do end)
puts defined?(1 + 2)
puts defined?(case 1 when 1 then 2 end)
```

```
expression
expression
method
expression
```

`if` ve `while` `"expression"` olarak bildirildi; yani bir yöntem çağrısı değil, dilin yapısıdır. `1 + 2` ise `"method"`. `case` de `"expression"` çıktı: dalın kendisi sözdizimidir, ama içindeki her `when` karşılaştırması bir iletiyi gönderir. Aynı yapıda iki katman var: `case` iskeleti sözdizimidir, dal seçimi ise nesnenin sınıfının yanıtladığı bir iletidir.

#### Deyim Değiştiriciler ve Değer Üreten if

Ruby'de bir koşulu ya da döngüyü cümlenin sonuna koymak mümkündür. Bunlara deyim değiştirici denir: `puts x if y`, `x += 1 until z`. Ayrıca `if` bir değer üretir; hiçbir dal çalışmazsa bu değer `nil` olur.

```ruby
# deyim_degistirici.rb — deyim degistiriciler ve if'in degeri
x = 3
puts "buyuk" if x > 2
x += 1 until x >= 6
puts x

y = if false then 1 end
p y
z = if true then 1 end
p z
```

```
buyuk
6
nil
1
```

İlk satır `x > 2` doğru olduğu için çalıştı. `until` değiştiricisi `x` altıya ulaşana kadar toplamayı tekrarladı. `if false then 1 end` hiçbir dalı çalıştırmadığı için `nil` döndü, `if true then 1 end` ise `1` döndü. Sonuç: `if` bir deyim değil, değer üreten bir ifadedir. Bu, atamanın da `7` değerini taşımasıyla aynı özelliktir.

#### Doğruluk ve Döngünün Değeri

Koşulun doğru ya da yanlış sayılması da dilin kendi kuralıdır. Ruby'de yalnız iki değer yanlış sayılır: `nil` ve `false`. Geri kalan her şey, `0` ve boş dizgi dahil, doğru sayılır. Aşağıdaki dosya beş değeri aynı koşulda dener.

```ruby
# dogruluk.rb — yalniz nil ve false yanlis sayilir
[nil, false, 0, "", []].each do |deger|
  durum = if deger then "dogru" else "yanlis" end
  printf("%-5s %s\n", deger.inspect, durum)
end
```

```
nil   yanlis
false yanlis
0     dogru
""    dogru
[]    dogru
```

`nil` ve `false` yanlış, `0`, `""` ve `[]` doğru çıktı. Bu kural dilin kendi kuralıdır: yalnız `nil` ve `false` yanlış sayılır, geri kalan her değer doğrudur. Döngüler de değer üretir. `while` döngüsü `break` ile bir değerle çıkarsa o değer döngünün sonucu olur; `break` olmadan biterse sonuç `nil`'dir.

```ruby
# dongu_degeri.rb — while da deger uretir
n = 0
sonuc = while true
  n += 1
  break n * 10 if n == 3
end
p sonuc

bos = while false do end
p bos
```

```
30
nil
```

Döngü üçüncü turda `30` ile çıktı ve `sonuc` bu değeri taşıdı. Hiç çalışmayan döngü ise `nil` verdi. Bu, `if`'in hiçbir dal çalışmadığında `nil` üretmesiyle aynı davranıştır: Ruby'de denetim yapıları ifadedir ve değeri vardır.

#### Döngüler ve Değer Üretmeleri

Önceki bölümler dalları ölçtü; bu bölüm döngüleri ölçer. Soru yine değerle ilgilidir: bir döngü ifadesi bir değer üretir mi, ve `break` o değeri belirleyebilir mi? Yanıtlayan ekseni bu bölümde iki yerde görünür: `while` ve `until` anahtar sözcüktür ve sözdizimidir, `loop` ise bir yöntemdir ve sahibi çıktıda okunur. İkinci blok ise bir kapsam farkına bakar: `for` ile blok değişkeni dışarıya aynı biçimde mi davranır?

```ruby
# donguler.rb — dongulerin degeri ve next
sonuc = loop { break 5 }
puts sonuc
i = 0
i += 1 while i < 3
puts i
j = 10
j -= 1 until j < 8
puts j
bos = while false do end
puts bos.inspect
toplam = 0
(1..5).each do |n|
  next if n.even?
  toplam += n
end
puts toplam
puts method(:loop).owner
```

```
5
3
7
nil
9
Kernel
```

`loop { break 5 }` döngünün değeri olarak `5` verdi: `break` argümanı döngü ifadesinin sonucu olur. `i += 1 while i < 3` değiştiricisi `i`'yi `3`'e çıkardı, `j -= 1 until j < 8` ise `j`'yi `7`'de durdurdu; `while` koşul doğru kaldıkça, `until` koşul yanlış kaldıkça sürer. `while false do end` ifadesi hiç çalışmadığı hâlde bir değer üretti ve bu değer `nil`'dir. Yani döngü de, `if` gibi, bir ifadedir ve değer taşır. `(1..5).each` içinde `next if n.even?` çift sayıları atladı; kalan `1`, `3`, `5` toplandı ve `9` çıktı. `next` yinelemeyi atlar, `break` döngüyü bitirir; ikisi de blok içinde yazılır ve ikisi de dilin anahtar sözcüğüdür; bu dosya onlar için bir sahip basmadı. Son satır `loop`'un sahibinin `Kernel` olduğunu gösterdi: `loop` bir anahtar sözcük değil, `puts` gibi bir yöntemdir.

```ruby
# for_sizintisi.rb — for degiskeni disari sizar, blok degiskeni sizmaz
[1, 2].each { |k| k }
d = defined?(k)
puts d.inspect
for m in [1, 2] do end
puts defined?(m)
puts m
puts [1, 2].method(:each).owner
puts 1.upto(3).to_a.inspect
puts 3.times.class
```

```
nil
local-variable
2
Array
[1, 2, 3]
Enumerator
```

`each` bloğunun değişkeni `k` dışarıdan sorulduğunda `nil` döndü: blok değişkeni bloğun içinde kalır. `for m in [1, 2] do end` ise `m`'yi dışarıya sızdırdı: `defined?(m)` `"local-variable"` bildirdi ve `m` sonunda `2` oldu. Yani `for` döngüsü yeni bir kapsam açmaz. Aynı işi gören iki yazımın kapsam davranışı farklıdır; bu fark, hangisinin seçildiğini bir tercih olmaktan çıkarıp bir sonuca bağlar. `[1, 2].method(:each).owner` `Array` verdi; yani `each`'i dizinin sınıfı karşılar. `1.upto(3).to_a` `[1, 2, 3]` verdi ve bloksuz çağrılan `3.times` bir `Enumerator` döndürdü: bloksuz çağrılan `times` değer üretmek yerine bir yineleyici verir.

**DT11.** `for`'un arka planda dizinin `each` iletisini çağırdığı ölçülmedi. Ölçülen yalnızca kapsam farkı ve `each`'in sahibinin `Array` olduğudur.

#### Kısa Devre ve Karşılaştırma

Koşullar yalnız `if` ve `case` ile kurulmaz: `||` ve `&&` işleçleri de sağ tarafı çalıştırıp çalıştırmamaya karar verir. Bu bölüm iki soruyu ölçer: bir işleç sağ tarafı değerlendirmeyi atladığında ne gözlenir, ve karşılaştırma yapan yöntemlerin sahibi kim? Eksen yine yanıtlayandır; `||` ve `&&` dilin sözdizimidir, `<=>` ve `between?` ise iletidir.

```ruby
# kisa_devre.rb — sag taraf calisir mi, kim karsilar
def iz(ad)
  puts ad
  true
end
iz("a") || iz("b")
iz("c") && iz("d")
v = nil || "varsayilan"
puts v
w = false && 1
puts w.inspect
puts 1 <=> 2
puts 5.between?(1, 10)
n = 5
durum = n > 3 ? "buyuk" : "kucuk"
puts durum
puts 5.method(:<=>).owner
puts 5.method(:between?).owner
```

```
a
c
d
varsayilan
false
-1
true
buyuk
Integer
Comparable
```

`iz("a") || iz("b")` satırında yalnız `a` basıldı: sol taraf doğru olduğu için sağ taraf hiç çalışmadı. `iz("c") && iz("d")` satırında ise `c` ve `d` ikisi de basıldı, çünkü `&&` sol taraf doğru olunca sağ tarafa da bakar. Kısa devre budur: sonuç sol tarafla belirlenmişse sağ taraf değerlendirilmez. `nil || "varsayilan"` ifadesi `varsayilan` verdi; `||` yalnız doğruluk değeri üretmez, işleneninin kendisini döndürür. `false && 1` ise `false` döndürdü. `||` ile varsayılan değer vermek bu davranışa dayanır: sol taraf `nil` ya da `false` ise sağ taraftaki değer döner.

`1 <=> 2` `-1` verdi: karşılaştırma iletisi sıralamayı bir tam sayı olarak bildirir. `5.between?(1, 10)` `true` oldu. Üçlü işleç `n > 3 ? "buyuk" : "kucuk"` `buyuk` verdi. Sahip satırları iki ayrı yanıtlayanı ortaya çıkardı: `<=>` iletisini `Integer` karşılar, `between?` iletisini ise `Comparable` modülü karşılar. İkisi de bir sayıya gönderilir ve sonucu verir, ama tanımlandıkları yer farklıdır. Yani koşul yazarken kullanılan karşılaştırma iletileri, sınıfın kendi yöntemleri ile ortak bir modülün yöntemleri arasında bölünmüştür.

#### Hata Akışı: rescue, else ve ensure

Denetim akışı yalnız koşul ve döngüden ibaret değildir; hata da akışı değiştirir. Bu bölüm `rescue`, `else` ve `ensure` dallarının hangi sırayla çalıştığını ve yöntemin değerini hangisinin belirlediğini ölçer. Hata sınıfı basılır, iletisi basılmaz. Eksen sözdizimidir: `rescue` ve `ensure` anahtar sözcüktür, `raise` ise bir yöntemdir.

```ruby
# istisna_akisi.rb — rescue, else ve ensure sirasi
def dene
  yield
rescue ZeroDivisionError => e
  puts e.class
  :kurtarildi
else
  :sorunsuz
ensure
  puts "temizlik"
end
a = dene { 1 / 0 }
puts a.inspect
b = dene { 1 / 1 }
puts b.inspect
c = begin
  Integer("x")
rescue ArgumentError
  -1
end
puts c
puts method(:raise).owner
puts method(:dene).owner
```

```
ZeroDivisionError
temizlik
:kurtarildi
temizlik
:sorunsuz
-1
Kernel
Object
```

İlk çağrıda blok `1 / 0` hesapladı ve `ZeroDivisionError` fırlattı. `rescue` dalı çalıştı, sınıfı bastı ve `:kurtarildi` döndürdü; ardından `ensure` dalı `temizlik` yazdı. Dönüş değeri `:kurtarildi` olarak kaldı: `ensure` yöntemin değerini belirlemez. İkinci çağrıda hata çıkmadı, bu yüzden `else` dalı çalıştı ve yöntemin değeri `:sorunsuz` oldu; `ensure` yine `temizlik` yazdı. Yani `ensure` her iki yolda da çalışır, `rescue` yalnız hata olduğunda, `else` yalnız hata olmadığında çalışır.

`begin` bloğu da bir ifadedir ve değer taşır. `Integer("x")` `ArgumentError` fırlattı, `rescue` dalı `-1` döndürdü ve `c` bu değeri aldı. Bu, `if`'in değer üretmesinin hata akışındaki karşılığıdır: dallardan biri çalışır ve çalışan dalın son ifadesi bütünün değeri olur. Sahip satırları `raise` için `Kernel`, `dene` için `Object` gösterdi: hata fırlatmak bir yöntem çağrısıdır, ama `rescue` ile `ensure` dilin kendi yazımıdır. Hata iletisinin metni bu bölümde basılmadı; çünkü iletinin metni sürüme göre değişebilir, sınıfı ise değişmez.

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

Bu dersin yanıtlayanı sınıftır. Burada kayma, `case`'in yanıtlayanını `case`'e verilen değerin sınıfı sanmaktır. Oysa `case 7` ifadesinde `7`'nin sınıfı `Integer`'dır, ama `===` iletisinin alıcısı dal nesnesidir. Bu yüzden aynı `7` için `when Integer` satırında `Module`, `when /a/` satırında `Regexp`, `when 1..10` satırında `Range` yanıtlar.

```ruby
# dal_nesnesi.rb — case'in yanitlayani dalin nesnesinin sinifidir
dallar = [Integer, /a/, 1..10]

dallar.each do |dal|
  printf("%-8s sinifi %-8s ileti sahibi %s\n", dal.inspect, dal.class, dal.method(:===).owner)
end

puts Integer === 7
puts(/a/ === "kalem")
puts dallar[2] === 7
```

```
Integer  sinifi Class    ileti sahibi Module
/a/      sinifi Regexp   ileti sahibi Regexp
1..10    sinifi Range    ileti sahibi Range
true
true
true
```

Üç dal nesnesi, üç ayrı sahip: `Integer` için `===` iletisini `Module` yanıtlar (önceki dersteki `Integer.===` gözlemi), düzenli ifade için `Regexp`, aralık için `Range`. `case`'in değeri hep `7` ya da aynı `"kalem"` olsa bile değişen yalnız alıcıdır. Sınıf yanıtlayanı olarak okunacak olan, `case` değerinin değil, dal nesnesinin sınıfıdır.

Bu derste ölçülen iki katmanı birlikte okumak gerekir. Denetim yapısının iskeleti, yani `if`, `while` ve `case` sözcükleri, dilin kendi yazımıdır ve `defined?` onları `"expression"` olarak bildirir. Dalın içindeki karşılaştırma ise bir iletidir ve nesnenin sınıfı onu yanıtlar. İkisini ayırmak, bir `case` ifadesini okurken "bu dal neden eşleşti" sorusunu "dal nesnesinin `===` yöntemi ne yapıyor" sorusuna çevirir.

#### Özet

- `case` her `when` dalında dal nesnesine `===` iletisi gönderir; `when 1..10` perde arkasında `(1..10) === 7` sorusudur.
- `===` iletisinin alıcısı dal nesnesi, argümanı `case` değeridir; sıra tersine çevrilirse yanıtlayan değişir.
- `if` ve `while` `defined?` ile `"expression"` bildirilir; dilin kendi yapısıdır ve ileti göndermez.
- Deyim değiştiriciler (`if`, `unless`, `while`, `until`) bir cümleyi koşullu ya da döngülü yapar; `if` bir değer üretir ve hiçbir dal çalışmazsa `nil` döner.
- Bu dersin yanıtlayanı sınıftır: `case`'in yanıtlayanı, `case` değerinin değil dal nesnesinin sınıfıdır.
- `loop { break 5 }` `5` döndürür, `while false do end` `nil` verir; `for` değişkeni dışarı sızar, blok değişkeni sızmaz; `||` ve `&&` sağ tarafı gerekmedikçe çalıştırmaz, `ensure` her yolda çalışır.

#### Sonraki Adım

Koşul ve dal seçimini kimin yanıtladığını ölçtük. Peki bir yöntem çağrısında alıcı hiç yazılmadığında, örneğin `puts` ya da `format` yazıldığında, iletiyi kim karşılıyor? Sonraki ders, Yöntemler, alıcısız çağrının yanıtlayanını ölçer.

### Yöntemler

Source: https://academia.sh/tr/kurslar/ruby-temelleri/yontemler

Alıcısız çağrıların Kernel modülünde bulunması; varsayılan değer, değişken sayıda argüman ve anahtarlı argümanlı yöntem tanımı; örtük dönüş; üst düzey def'in Object'e yazılması ve özel olması.

Önceki ders `case`'in dal seçerken dal nesnesine bir ileti gönderdiğini gösterdi ve bir soru bıraktı: `format("%03d", 7)` ya da `puts "ad"` yazıldığında ortada bir alıcı yok. İleti nereye gidiyor, onu kim yanıtlıyor? Fonksiyon kavramı Programlama Temelleri kursunda (M01/K02) tanıtılmıştı; burada o kavram tekrarlanmaz. Bu ders Ruby'nin yöntemlerini tek bir ölçüyle okur: alıcısız bir çağrıyı karşılayan sahip kim?

#### Alıcısız Çağrıyı Kim Karşılar

`format("%03d", 7)` ifadesinde nokta yok, alıcı yok. Ama önceki derslerde öğrendik: Ruby'de her çağrı bir iletidir ve iletinin bir alıcısı vardır. Alıcı yazılmadığında alıcı, kodun o anda çalıştığı nesnedir. Üst düzeyde bu nesne dosyanın ana nesnesidir. Aşağıdaki dosya bu çağrının yöntem olduğunu `defined?` ile, sahibini `method` ve `owner` ile sorar.

```ruby
# alicisiz.rb — alicisiz cagrinin yanitlayani
puts defined?(format("%03d", 7))
puts method(:format).owner
puts method(:format).owner.instance_of?(Class)
puts format("%03d", 7)
```

```
method
Kernel
false
007
```

`format` bir yöntemdir ve sahibi `Kernel`'dir. `Kernel` bir `Class` değil, bir modüldür, bu yüzden `instance_of?(Class)` `false` döndü. Kursun sınıflandırma kuralı bu durumda yanıtlayanı modül sayar. Dosyanın üst düzeyindeki nesne de `Kernel` yöntemlerine ulaşır, o yüzden alıcı yazılmasa da çağrı bir sahip bulur.

**DT12.** Bu derste "yöntem", `def` ile ya da dil tarafından tanımlanmış, adıyla çağrılan iş birimidir. `method(:ad).owner` yöntemin sahibini, yani tanımının bulunduğu sınıf ya da modülü verir. Arama zincirinin kendisi (hangi sırayla bakıldığı) bu kursta ölçülmez; yalnız sonuç, yani sahip basılır.

#### Kernel'deki Tanıdık Çağrılar

Önceki derslerde `puts` ve `p` hiçbir açıklama yapılmadan kullanıldı. Aşağıdaki dosya altı çağrının hepsinin sahibini sorar.

```ruby
# kernel_cagrilari.rb — alicisiz cagrilarin sahibi
[:puts, :p, :format, :Integer, :raise, :block_given?].each do |ad|
  printf("%-14s %s\n", ad, method(ad).owner)
end
```

```
puts           Kernel
p              Kernel
format         Kernel
Integer        Kernel
raise          Kernel
block_given?   Kernel
```

Altısı da `Kernel`'dedir. `puts` ve `p` ekrana yazar, `format` biçimler, `Integer` dönüştürür, `raise` bir istisna fırlatır, `block_given?` ise yöntemin bir bloğu olup olmadığını söyler. Hepsi "dil sözcüğü" gibi görünür ama aslında sıradan bir yöntemdir ve sahibi bellidir. Bu, önceki dersteki `if` ve `while`'dan farklıdır: onlar sözdizimiydi, bunlar iletidir.

#### Parametre Türleri ve Örtük Dönüş

Kendi yöntemimizi `def` ile yazarız. Parametre listesinde zorunlu argümanlar, varsayılan değerler, değişken sayıda argüman ve anahtarlı argümanlar bir arada bulunabilir. Yöntem `return` yazılmasa da son ifadenin değerini döndürür; buna örtük dönüş denir.

```ruby
# parametreler.rb — parametre turleri ve ortuk donus
def birlestir(ad, unvan = "bay", *ekler, sehir: "ankara")
  "#{unvan} #{ad} #{ekler.size} #{sehir}"
end

puts birlestir("ali")
puts birlestir("ali", "dr", 1, 2, sehir: "izmir")
p method(:birlestir).parameters
```

```
bay ali 0 ankara
dr ali 2 izmir
[[:req, :ad], [:opt, :unvan], [:rest, :ekler], [:key, :sehir]]
```

İlk çağrıda yalnız `ad` verildi; `unvan` ve `sehir` varsayılan değerlerini aldı, `ekler` boş kaldı. İkinci çağrıda `"dr"` varsayılanı ezdi, `1` ve `2` değişken sayıda argüman olarak `ekler` dizisine toplandı ve `sehir:` anahtarlı argüman olarak verildi. `parameters` her birinin türünü etiketler: zorunlu `req`, varsayılanlı `opt`, değişken sayıda `rest`, anahtarlı `key`. Yöntemde `return` yok; son ifade olan dizgi döndü. Dizgideki `#{...}` yazımı ifade yerleştirmedir.

#### Blok Alan Yöntemler

Bir yöntem çağrısına süslü parantez ya da `do ... end` ile bir blok verilebilir. Yöntem bloğu `yield` ile çalıştırır, bloğun verilip verilmediğini `block_given?` ile sorar. `block_given?`'ın sahibi iki bölüm önce `Kernel` çıkmıştı: bu da sıradan bir yöntemdir. Bloğu olmayan çağrıda yöntem farklı bir değer döndürebilir. Ayrıca zorunlu argüman eksik verilirse ne olacağı da ölçülebilir.

```ruby
# blok_alan.rb — yield ve block_given?
def tekrar(adet)
  return :bos unless block_given?
  adet.times { |sira| yield sira }
  :bitti
end

p tekrar(2) { |sira| puts sira }
p tekrar(2)

begin
  tekrar
rescue ArgumentError => e
  puts e.class
end
```

```
0
1
:bitti
:bos
ArgumentError
```

Blok verilen çağrıda blok iki kez çalıştı (`0` ve `1` yazıldı) ve yöntem `:bitti` döndürdü. Blok verilmeyen çağrıda `block_given?` `false` olduğu için yöntem `:bos` ile erken döndü. Zorunlu `adet` argümanı verilmeyen çağrı `ArgumentError` fırlattı ve yalnız sınıfı basıldı. Yani bir yöntemin arayüzü yalnız parametre listesi değildir; blok da çağrının parçasıdır, ama bu yöntemin parametre listesinde görünmez. Blok `&blok` biçiminde açıkça adlandırılırsa listeye girer; bunu sonraki bölümün `:block` etiketi gösterir.

#### Birden Çok Değer Döndürmek

Ruby'de bir yöntem tek değer döndürür, ama o değer bir dizi olabilir ve çağıran taraf diziyi tek hamlede parçalara ayırabilir. Bu da örtük dönüşle birlikte çalışır: yöntemin son ifadesi köşeli parantezli iki elemanlı bir dizidir.

```ruby
# cok_donus.rb — dizi donduren yontem ve coklu atama
def bol(a, b)
  [a / b, a % b]
end

sonuc = bol(17, 5)
p sonuc
bolum, kalan = bol(17, 5)
puts bolum
puts kalan
puts sonuc.class
```

```
[3, 2]
3
2
Array
```

Yöntem tek bir `Array` döndürdü. `bolum, kalan = ...` yazımı bu diziyi iki adda açar; bu bir yöntem özelliği değil, atamanın yazımıdır ve önceki dersteki sözdizimi gözlemine bağlanır. Böylece yöntemin dönüş değeri ile çağıranın bunu nasıl okuduğu iki ayrı iş olarak kalır.

#### Parametre Türlerini Yoklamak

Bu bölüm parametre türlerini ölçer. Aşağıdaki yöntemde yedi parametre türü bir arada durur: zorunlu, varsayılanlı, artan (`*rest`), zorunlu anahtarlı, isteğe bağlı anahtarlı, anahtar toplayan (`**opts`) ve blok alan (`&blok`). Ölçü, yöntemin kendi kendini anlatmasıdır: `method(:ad).parameters` yöntemin parametre listesini, `arity` ise kaç argüman beklediğini bildirir. Eksen sahiptir; tanımlanan yöntemin sahibi de basılır.

```ruby
# parametre_turleri.rb — parametre turleri ve arity
def ornek(a, b = 2, *rest, c:, d: 4, **opts, &blok); end
puts method(:ornek).parameters.inspect
puts method(:ornek).arity
def iki(a, b); end
puts method(:iki).arity
def secenekli(a, b = 1); end
puts method(:secenekli).arity
puts method(:ornek).owner
```

```
[[:req, :a], [:opt, :b], [:rest, :rest], [:keyreq, :c], [:key, :d], [:keyrest, :opts], [:block, :blok]]
-3
2
-2
Object
```

`parameters` çıktısı her parametreyi bir tür etiketiyle verdi: `:req` zorunlu, `:opt` varsayılanlı, `:rest` artan, `:keyreq` zorunlu anahtarlı, `:key` isteğe bağlı anahtarlı, `:keyrest` anahtar toplayan, `:block` blok alan parametredir. `arity` değerleri üç ayrı durumu gösterdi: iki zorunlu parametreli `iki` için `2`, bir zorunlu ve bir varsayılanlı `secenekli` için `-2`, `ornek` için `-3`. Negatif sayı "sabit bir sayıda argüman değil" demektir; isteğe bağlı ya da sınırsız parametre vardır. Mutlak değer, en az gereken argüman sayısının bir fazlasıdır: `-2` en az bir, `-3` en az iki argüman ister. `ornek`'te zorunlu `a`'nın yanında zorunlu anahtarlı `c:` ve isteğe bağlı parametreler bulunur ve sonuç `-3` oldu. Zorunlu anahtarlı parametre bu hesapta tek bir ek argüman sayılır. Kural burada yalnız bu üç yöntemde ölçüldü. Son satırda `ornek`'in sahibi `Object` çıktı: üst düzeyde tanımlanan yöntemin yanıtlayanı `Object`'tir.

**DT13.** `arity` değerinin işareti yalnız bu üç yöntemde okundu. Başka parametre dizilimleri için aynı kuralın geçtiği ölçülmedi.

```ruby
# anahtar_hatasi.rb — eksik ve fazla anahtar ile ortuk donus
def kutu(en:, boy: 1)
  en * boy
end
begin
  kutu(boy: 2)
rescue ArgumentError => e
  puts e.class
end
begin
  kutu(en: 1, derinlik: 3)
rescue ArgumentError => e
  puts e.class
end
puts kutu(en: 3)
def dolayli
  42
end
def acik
  return 42
end
puts dolayli == acik
def erken(n)
  return :negatif if n < 0
  :pozitif
end
puts erken(-1).inspect
puts erken(1).inspect
puts method(:kutu).owner
```

```
ArgumentError
ArgumentError
3
true
:negatif
:pozitif
Object
```

`kutu(boy: 2)` zorunlu `en:` anahtarı olmadan çağrıldı ve `ArgumentError` fırlattı; `kutu(en: 1, derinlik: 3)` tanımsız bir anahtar verildiği için yine `ArgumentError` fırlattı. İki durumda da yalnız `e.class` basıldı, çünkü hata iletisinin metni sürüme bağlı kalabilir. Geçerli çağrı `kutu(en: 3)` `3` verdi. Bu üç sonuç anahtarlı parametrelerin bir sözleşme olduğunu gösterir: eksik de fazla da aynı sınıfla reddedilir. İkinci yarıda örtük dönüşle `return` karşılaştırıldı: `dolayli` ile `acik` aynı `42` değerini döndürdü. `return` gereksiz değildir; `erken(-1)` satırı `return`'ün yöntemi erken bitirdiğini, `erken(1)` satırı ise son ifadenin değerinin dönüş değeri olduğunu gösterir. Yöntemin sahibi `kutu` için de `Object` çıktı. Böylece dönüş değeri iki yoldan belirlenir: yöntemin son ifadesi ya da `return` ile verilen değer.

#### Yöntemi Nesne Olarak Tutmak

Yöntem de bir nesneye dönüştürülebilir. `method(:ad)` çağrısı yöntemi `Method` sınıfından bir nesne olarak verir; bu nesne çağrılabilir, bir bloğa dönüştürülebilir ve alıcısı ile sahibi sorulabilir. Bu bölümün sorusu, üst düzeyde tanımlı bir yöntemin kime ait olduğudur ve dışarıdan sorulduğunda herkese açık görünüp görünmediğidir. Eksen sahiptir; alıcıyı ve sahibi birlikte basıyoruz.

```ruby
# yontem_nesnesi.rb — yontemi nesne olarak tutmak
def kare(n)
  n * n
end
m = method(:kare)
puts m.class
puts m.call(4)
puts [1, 2, 3].map(&m).inspect
puts m.name.inspect
puts m.receiver == self
puts m.owner
puts send(:kare, 3)
puts respond_to?(:kare, true)
puts respond_to?(:kare)
```

```
Method
16
[1, 4, 9]
:kare
true
Object
9
true
false
```

`method(:kare)` bir `Method` nesnesi verdi. `m.call(4)` `16` döndürdü; yöntem bir nesne olduğu için bir değişkende tutulup sonra çağrılabildi. `[1, 2, 3].map(&m)` ifadesi `&` ile yöntem nesnesini bloğa dönüştürdü ve `[1, 4, 9]` verdi. Ad `:kare` bir semboldür. `m.receiver == self` `true` oldu: üst düzeyde tanımlanan yöntemin alıcısı, üst düzeydeki `main` nesnesidir ve `m.owner` `Object` çıktı. Böylece alıcısız bir `kare(3)` çağrısının neden çalıştığı da açıklanır: alıcı yazılmadığında ileti, o anki `self` nesnesine gider.

Son üç satır erişim farkını ölçer. `send(:kare, 3)` `9` verdi, yani iletiyi adıyla göndermek çalıştı. `respond_to?(:kare, true)` `true`, `respond_to?(:kare)` ise `false` bastı. İkinci argüman özel yöntemleri de saymayı söyler; argümansız soru yalnız herkese açık yöntemleri sayar ve üst düzey yöntem bunların arasında değildir. Yani `kare`, `Object` sınıfında tanımlıdır ve herkese açık yöntemler arasında sayılmaz, ama alıcısız çağrıyla ya da `send` ile ulaşılabilir. Alıcı yazılarak yapılan bir çağrının ne verdiği burada ölçülmedi. Bu fark `Object` sahibinin ve erişim düzeyinin ayrı iki özellik olduğunu gösterir.

**DT14.** Üst düzey yöntemin özel olduğu `respond_to?` çiftinden çıkarıldı; erişim düzeyi başka bir ölçümle (`private_methods` gibi) doğrulanmadı.

#### Çağrı Tarafında Splat

Parametre tanımında görülen `*` ve `**` işaretleri, çağrı tarafında tersini yapar: bir diziyi ya da karma yapıyı argüman listesine açarlar. Bu bölüm iki yönü birlikte ölçer. Eksen sahiptir; bir yöntemin kaç argüman bekleyebileceği `arity` ile, kimin tanımladığı `owner` ile basılır.

```ruby
# splat_cagri.rb — cagri tarafinda * ve **
def topla(*sayilar)
  sayilar.sum
end
dizi = [1, 2, 3]
puts topla(*dizi)
puts topla(dizi.first, *dizi.last(2))
puts topla
def secenek(**o)
  o.inspect
end
h = {a: 1}
puts secenek(**h)
puts secenek(b: 2, **h)
def tek(x)
  x
end
puts tek([1, 2]).inspect
puts method(:topla).arity
puts method(:topla).owner
```

```
6
6
0
{a: 1}
{b: 2, a: 1}
[1, 2]
-1
Object
```

`topla(*dizi)` diziyi üç ayrı argümana açtı ve `6` verdi. `topla(dizi.first, *dizi.last(2))` yazımı bir tek argümanla açılmış iki argümanı birleştirdi ve yine `6` çıktı. Argümansız `topla` çağrısı `0` verdi: artan parametre boş bir diziyi kabul eder. `secenek(**h)` karma yapıyı anahtarlı argümanlara açtı ve `{a: 1}` verdi; `secenek(b: 2, **h)` açık yazılmış bir anahtarla açılmış anahtarı birleştirdi ve `{b: 2, a: 1}` oldu. Birleşmede açık yazılan anahtar önce geldi.

Karşıt örnek `tek([1, 2])` çağrısıdır: yıldız olmadan verilen dizi tek argüman olarak kaldı ve yöntem `[1, 2]` döndürdü. Yani açma işlemi çağrı tarafında bilerek yazılır; yıldız olmayan yerde dizi bir dizidir. `method(:topla).arity` `-1` verdi: sabit sayıda argüman beklenmez ve en az sıfır tanesi gerekir. Sahip `Object` çıktı. Bir çağrının argümanlarını dilin yazımı açar, ama açılmış listeyi karşılayan yine yöntemin parametre tanımıdır; iki taraf birbirine uyduğunda çağrı çalışır. Parametre tanımı ve çağrı tarafı aynı işareti kullanır, ama yönleri zıttır: tanımda `*` argümanları toplar, çağrıda `*` diziyi açar.

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

Bu dersin yanıtlayanı modüldür, ama bir kayma var: kendi yazdığımız yöntemin sahibi bir modül mü olur? Dosyanın üst düzeyinde `def` yazıldığında yöntem nereye gider? Aşağıdaki ölçüm bunu sorar.

```ruby
# ust_duzey.rb — ust duzey def Object'e yazilir ve ozeldir
def kare(n)
  n * n
end

puts method(:kare).owner
puts method(:kare).owner.instance_of?(Class)
puts respond_to?(:kare)
puts respond_to?(:kare, true)
puts kare(4)
```

```
Object
true
false
true
16
```

Üst düzey `def kare` yöntemi `Object` sınıfına yazar. `Object` bir `Class` örneğidir (`true`), yani bu durumda yanıtlayan modül değil, sınıftır. Yöntem aynı zamanda özel kalır: `respond_to?(:kare)` `false` döndü, çünkü özel yöntemler dışarıdan sorulduğunda görünmez. `respond_to?(:kare, true)` özel yöntemleri de kapsar ve `true` verir. Alıcısız `kare(4)` çağrısı ise `16` döndürdü, çünkü alıcı yazılmadığında özel yöntemlere de erişilir. `Kernel`'deki yöntemler modülün sahipliğindedir; kendi `def`'imiz ise `Object`'in, yani bir sınıfın sahipliğindedir.

#### Özet

- Alıcısız çağrıda alıcı, kodun o anda çalıştığı nesnedir; `format("%03d", 7)` çağrısını `Kernel` modülü yanıtlar.
- `puts`, `p`, `format`, `Integer`, `raise` ve `block_given?` sıradan yöntemlerdir ve altısının da sahibi `Kernel`'dir.
- Bir yöntem zorunlu, varsayılan değerli, değişken sayıda ve anahtarlı argümanı birlikte taşıyabilir; `return` yazılmazsa son ifade döner.
- Üst düzey `def` yöntemi `Object` sınıfına yazar ve özel bırakır; `respond_to?(:kare)` `false`, `respond_to?(:kare, true)` `true` döner.
- Bu dersin yanıtlayanı modüldür: alıcısız çağrıların sahibi `Kernel` modülüdür, kendi yazdığımız üst düzey yöntemin sahibi ise sınıf olan `Object`'tir.
- `method(:ad).parameters` ölçülen yöntemdeki yedi parametre türünü adlandırır; eksik ya da tanımsız anahtar `ArgumentError` verir; çağrı tarafında `*` ve `**` diziyi ve karma yapıyı argümanlara açar.

#### Sonraki Adım

Yöntemlerin sahibi ölçüldü ve alıcısız çağrının nereye gittiği görüldü. Sırada dizgiler var: `"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, ve bir sihirli yorum bunu değiştiriyor mu? Sonraki ders, Semboller ve Dizgiler, bunu ölçer.

### Semboller ve Dizgiler

Source: https://academia.sh/tr/kurslar/ruby-temelleri/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.

Ö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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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?

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

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

def yorumsuz_sahip
  "ad".method(:upcase).owner
end
```

```ruby
# 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.

## Koleksiyonlar ve Bloklar

Diziler ve karma yapılar, bir yönteme davranış geçiren bloklar, ortak yineleme arayüzü olarak Enumerable, proc ile lambda arasındaki fark, istisnaların yakalanması ve yeniden denenmesi ve dosyalarla metin işleme akışları.

### Diziler ve Karma Yapılar

Source: https://academia.sh/tr/kurslar/ruby-temelleri/diziler-ve-karma-yapilar

Ruby'nin iki temel koleksiyonu olan dizi ve karma yapının hangi yöntemleri kendisinin, hangilerini ortak bir modülün yanıtladığı ve eksik bir anahtarın değerini bloğun belirlediği ölçülür.

Semboller ve Dizgiler dersi, `"ad".to_sym` iletisini `String` sınıfının yanıtladığını ve elde edilen `:ad` sembolünün, aynı değeri tek bir nesne olarak taşıdığı için `equal?` ile karşılaştırılabildiğini ölçtü. Sembolün asıl işi o derste henüz görünmedi: karma yapıda anahtar olmak. Bu ders iki temel koleksiyonu, diziyi ve karma yapıyı, ve aynı kursun sorusunu bu koleksiyonlara yöneltiyor: bir yönteme gelen **iletiyi kim yanıtlıyor**?

Kursun dört yanıtlayanı değişmez: **sınıf**, **modül**, **blok** ve **sözdizimi**. Düzyazıda ölçü kategorisi kastedildiğinde "yanıtlayan sınıf" yazılır; çıplak "sınıf" Ruby'nin `Class`'ıdır. Değişkenler, tipler ve döngüler M01/K02'de kavram olarak kuruldu; burada dizi **(array)**, sıralı ve sıfırdan numaralanan bir eleman listesidir. **Karma yapı (hash)** ise anahtardan değere giden bir eşlemedir ve elemanlarını eklenme sırasıyla tutar.

#### Temel İşlemler

İlk ölçüm yalnız davranışı gösterir. Dizi köşeli parantezle yazılır, `<<` sona eleman ekler, negatif indis sondan sayar, aralık bir dilim verir. Karma yapı süslü parantez ve `anahtar: değer` biçimiyle yazılır; bu biçimde anahtarlar semboldür.

```ruby
# temel.rb — dizi ve karma yapinin temel islemleri
dizi = [10, 20, 30]
dizi << 40
puts "ilk=#{dizi[0]} son=#{dizi[-1]} adet=#{dizi.size} disarda=#{dizi[10].inspect}"
puts "dilim=#{dizi[1..2].inspect} ilk_iki=#{dizi.first(2).inspect}"

karma = { ad: "Ayse", yas: 30 }
karma[:sehir] = "Izmir"
puts "ad=#{karma[:ad]} yok=#{karma[:mail].inspect} anahtarlar=#{karma.keys.inspect}"
karma.each { |anahtar, deger| puts "#{anahtar} -> #{deger}" }
puts karma.select { |_, deger| deger.is_a?(String) }.inspect
puts karma.map { |anahtar, deger| anahtar }.inspect
```
```
ilk=10 son=40 adet=4 disarda=nil
dilim=[20, 30] ilk_iki=[10, 20]
ad=Ayse yok=nil anahtarlar=[:ad, :yas, :sehir]
ad -> Ayse
yas -> 30
sehir -> Izmir
{ad: "Ayse", sehir: "Izmir"}
[:ad, :yas, :sehir]
```

Dizi sınırının dışındaki indis hata vermez, `nil` döndürür; karma yapıda olmayan anahtar da öyle. Bu iki sessiz `nil` bu dersin ikinci yarısındaki sorunun zeminidir: eksik bir anahtarın sonucunu belirleyen şey nedir? Karma yapı eklenme sırasını korur; `anahtarlar` satırındaki sıra `sehir` atamasının sona eklendiğini gösteriyor. `select` ve `map` aynı iki parametreli blok biçimini aldı, ama biri karma yapı döndürdü, öteki dizi. Bunun nedeni sonraki bölümde ölçülen sahiplik ayrımıdır. **KB1.** Ölçümler sembol anahtarlı karma yapılarla yapıldı; dizgi anahtarlı karma yapılar bu derste ölçülmedi ve sonuçlar onlara taşınmaz.

#### Yöntemi Hangi Tanım Taşıyor

Aynı koleksiyon üzerinde yazılan iki yöntem, aynı biçimde çağrılsa da farklı yerlerde tanımlı olabilir. `method(:ad).owner`, o yöntemi tanımlayan sınıfı ya da modülü verir. Bu ders arama sırasını göstermez; yalnız sonucu, yani sahibi okur.

```ruby
# koleksiyon.rb — koleksiyon yontemlerinin sahibi kim
dizi = [1, 2, 3]
karma = { a: 1, b: 2 }

puts "dizi.size     #{dizi.method(:size).owner}"
puts "dizi.map      #{dizi.method(:map).owner}"
puts "karma.each    #{karma.method(:each).owner}"
puts "karma.select  #{karma.method(:select).owner}"
puts "karma.fetch   #{karma.method(:fetch).owner}"
puts "karma.map     #{karma.method(:map).owner}"
```
```
dizi.size     Array
dizi.map      Array
karma.each    Hash
karma.select  Hash
karma.fetch   Hash
karma.map     Enumerable
```

Ruby 4.0.7'de beş yöntemi bir sınıf karşıladı: `size` ve `map` `Array`'de, `each`, `select` ve `fetch` `Hash`'te tanımlı. Altıncısı farklı: `karma.map` `Hash`'te değil, `Enumerable` modülünde tanımlı. Yani aynı `map` adı dizide sınıfın, karma yapıda modülün yanıtıdır. Hangi yöntemin hangi tanımda durduğu sürümden sürüme taşınabilir; bu yüzden sonuç dilin sözü değil bu sürümün ölçümü olarak okunur. `Enumerable` modülü üçüncü derstir ve burada yalnız adı anılır. Önceki bölümde `select` karma yapı, `map` dizi döndürdü: `select` `Hash`'in kendi yöntemi olduğu için sonucu da karma yapı olarak kurar, `Enumerable`'dan gelen `map` ise her zaman dizi üretir.

#### Eksik Anahtarın Değerini Kim Veriyor

Temel işlemlerde `karma[:mail]` `nil` döndürdü. Bu davranışı değiştirmek için karma yapı kurulurken bir varsayılan değer verilebilir. İki biçimi vardır: `Hash.new(0)` sabit bir değer, `Hash.new { ... }` ise bir **blok** verir ve anahtar eksik kaldığında o blok koşar. Blok, anahtarı parametre olarak alabilir; böylece sonuç anahtara bağlı olabilir. M12/K02, PHP'de paylaşılan varsayılan bir değerin değişikliği nereye ulaştırdığını ölçmüştü; burada o soru sorulmuyor, yalnız varsayılan değeri kimin ürettiği ölçülüyor.

Kursun ortak yardımcısı `yanit.rb` önce birebir verilir. Bir ifadeyi sırayla üç soruyla sınıflandırır: `defined?` bir yöntem çağrısı mı diyor, iki farklı blok iki farklı sonuç veriyor mu, yöntemin sahibi bir `Class` mı. Çıktı bloğu olmadığı için yalnız diske yazılır.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

Şimdi çekirdeğin `varsayilan deger` gözlemi koşar. İki `Hash.new` bloğu aynı `[:x]` iletisine bakıyor: biri her zaman `0`, ötekisi anahtarın metin karşılığını döndürüyor. Ardından sabit varsayılanlı biçim ve `defined?`'in iki durumu basılır.

```ruby
# varsayilan.rb — eksik anahtarin degerini kim veriyor
require_relative "yanit"

sifir = Hash.new { 0 }
metin = Hash.new { |_, anahtar| anahtar.to_s }

tur, kanit = yanitlayan(defined?(Hash.new), bloklu: -> { [sifir[:x], metin[:x]] })
puts "#{tur}  #{kanit}"
puts Hash.new(0)[:x]
puts defined?(Hash.new)
puts defined?(Hash.new { 0 })
```
```
blok  0 / "x"
0
method
expression
```

Birinci satır, çekirdeğin `varsayilan deger` satırıdır: **blok** yanıtlayan. Aynı `[:x]` iletisi iki karma yapıda `0` ve `"x"` verdi; iletiyi alan nesnenin sınıfı aynıydı (`Hash`), fark yalnız verilen bloktaydı. Bu yüzden yanıtı sınıf ya da modül değil blok belirler. İkinci satırda sabit varsayılanlı `Hash.new(0)[:x]` da `0` verdi; orada bir blok yoktur, değer kurulurken verilmiştir.

Son iki satır kuralın bir inceliğini gösteriyor. `defined?(Hash.new)` `"method"` döndürür, ama bloklu çağrı olan `defined?(Hash.new { 0 })` `"expression"` döndürür. Çekirdek bu yüzden iletiyi **bloksuz** sorgular ve bloğun etkisini ikinci adımda ölçer; bloklu biçimi sorgulasaydı birinci adım iletiyi sözdizimi sayardı. **KB2.** Bloklu `defined?` sonucu Ruby 4.0.7'de ölçüldü; başka bir sürümde aynı sonuç varsayılmaz. Bu ayrıntı Bloklar dersinde adlandırılır.

#### Dizi İşlemleri

Temel İşlemler bölümü yalnız `<<` ve tek indisli erişimi yazdı. Dizinin sık kullanılan öteki işlemleri sona ekleme, sondan çıkarma ve dilimlemedir. Sonsuz uçlu aralık `a[1..]` ilk elemandan sonrasını verir, `first(2)` baştan iki eleman alır. `push` ve `<<` sona ekler; `pop` son elemanı hem diziden çıkarır hem döndürür.

```ruby
# dizi_islem.rb — dilimleme, ekleme ve cikarma
a = [5, 3, 8, 1]
puts "a[1..]=#{a[1..].inspect} a[-1]=#{a[-1]} first(2)=#{a.first(2).inspect}"
a.push(9)
a << 7
puts "sonra=#{a.inspect}"
puts "pop=#{a.pop} kalan=#{a.inspect}"
```
```
a[1..]=[3, 8, 1] a[-1]=1 first(2)=[5, 3]
sonra=[5, 3, 8, 1, 9, 7]
pop=7 kalan=[5, 3, 8, 1, 9]
```

Dilimleme ve `first(2)` birer dizi döndürdü ve `a`'yı değiştirmedi: `push` satırına gelindiğinde `a` hâlâ özgün dört elemanı taşıyordu. `push(9)` ve `<<` aynı işi yaptı ve dizi altı elemana çıktı. `pop` `7` döndürdü ve dizi beş elemana indi: `pop`'un iki etkisi vardır, bir değer döndürmesi ve diziyi değiştirmesi. **KB19.** Dilimin yeni bir dizi mi yoksa aynı belleğin bir görünümü mü olduğuna bakılmadı; yalnız yazdırılan değerler ölçüldü.

#### Yerinde Değiştiren Biçim

Birçok dizi yönteminin iki biçimi vardır: yeni bir dizi döndüren ve diziyi yerinde değiştiren. Sıralamada bu çift `sort` ve onun yerinde sıralayan biçimidir. Fark `equal?` ile ölçülür: iki değişken aynı nesneyi mi gösteriyor?

```ruby
# yerinde.rb — yeni dizi ile ayni dizinin degismesi
a = [3, 1, 2]
b = a.sort
puts "yeni=#{b.inspect} a=#{a.inspect} ayni_nesne=#{a.equal?(b)}"
c = a.sort!
puts "yerinde a=#{a.inspect} ayni_nesne=#{a.equal?(c)}"
puts "sahip=#{a.method(:sort!).owner}"
```
```
yeni=[1, 2, 3] a=[3, 1, 2] ayni_nesne=false
yerinde a=[1, 2, 3] ayni_nesne=true
sahip=Array
```

`sort` yeni bir dizi verdi: `b` sıralıydı, `a` özgün sırasında kaldı ve `equal?` `false` döndü. Yerinde sıralayan biçim `a`'nın kendisini sıraladı ve aynı nesneyi geri verdi: `a` `[1, 2, 3]` oldu, `equal?` `true`. Yerinde sıralayan biçimin sahibi `Array` olarak basıldı, yani yanıtlayan bir sınıftır; iki biçimin farkı yöntemin sonucunun yeni nesne mi yoksa alıcının kendisi mi olduğundadır. **KB20.** Ölçüm yalnız sıralama çiftine yapıldı; başka çiftlerin aynı kalıbı izlediği varsayılmaz.

#### Karma Yapıda Sıra ve İç İçe Erişim

Karma yapı eklenme sırasını korur ve `to_a` onu çift dizisine çevirir. İç içe karma yapılarda `dig` anahtarları sırayla izler; yol bir yerde kesilirse hata fırlatmak yerine `nil` döndürür.

```ruby
# karma_ek.rb — ekleme sirasi, to_a ve dig
k = { b: 1, a: 2 }
k[:c] = 3
puts k.keys.inspect
puts k.to_a.inspect
ic = { kullanici: { adres: { sehir: "Izmir" } } }
puts ic.dig(:kullanici, :adres, :sehir)
puts ic.dig(:kullanici, :telefon, :no).inspect
```
```
[:b, :a, :c]
[[:b, 1], [:a, 2], [:c, 3]]
Izmir
nil
```

`k` önce `b` ve `a` anahtarlarıyla kuruldu, `c` sona eklendi; `keys` bu sırayı, `to_a` aynı sırayı çiftler hâlinde verdi. Alfabetik bir sıralama yoktur. `dig` üç anahtarı izleyip `Izmir`'i buldu; ikinci çağrıda `:telefon` yoktu ve `dig` hata fırlatmadan `nil` verdi. **KB21.** Sıra davranışı Ruby 4.0.7'de ve yalnız bu örnekle ölçüldü.

#### Paylaşılan Varsayılan Nesne

Varsayılan değeri blok yerine bir nesne olarak vermek, `Hash.new([])` biçiminde, bir tuzak taşır: o nesne tektir ve her eksik anahtar için aynısı döner. M12/K02 paylaşılan varsayılan değerin davranışını başka bir dilde ölçmüştü; burada Ruby'deki çıktı yalnız okunur.

```ruby
# ortak.rb — paylasilan varsayilan nesne ile blok bicimi
ortak = Hash.new([])
ortak[:a] << 1
ortak[:b] << 2
puts "ortak=#{ortak.inspect} varsayilan=#{ortak.default.inspect} anahtarlar=#{ortak.keys.inspect}"
ayri = Hash.new { |h, k| h[k] = [] }
ayri[:a] << 1
ayri[:b] << 2
puts ayri.inspect
```
```
ortak={} varsayilan=[1, 2] anahtarlar=[]
{a: [1], b: [2]}
```

`ortak[:a] << 1` eksik anahtar için varsayılan diziyi aldı ve ona ekledi; karma yapıya bir anahtar atanmadı. Bu yüzden `ortak` boş kaldı, `anahtarlar` `[]` oldu ve varsayılan dizi `[1, 2]` oldu: `:a` ve `:b` aynı diziye yazdı. Blok biçimi ise her eksik anahtarda bloğu koşturup sonucu `h[k] = []` ile karma yapıya yazdı; iki anahtar iki ayrı dizi aldı. Eksik anahtarın değerini ilk biçimde tek bir nesne, ikinci biçimde her seferinde blok veriyor. **KB22.**

#### Karma Yapıyı Dönüştürmek

Karma yapıdan yeni bir karma yapı ya da tek bir değer üretmek sık gerekir. `transform_values` değerleri dönüştürür, `to_h` bloğuyla anahtarları da değiştirebilir, `sum` ise `Enumerable` üzerinden gelir. Dizi tarafında `compact` `nil` elemanları atar, `uniq` yinelenenleri eler, `flatten` iç içe diziyi düzleştirir.

```ruby
# donustur.rb — karma yapi donusumleri ve dizi temizligi
k = { a: 1, b: 2 }
puts k.transform_values { |v| v * 10 }.inspect
puts k.to_h { |a, v| [a.to_s, v] }.inspect
puts k.sum { |_, v| v }
puts k.min_by { |_, v| -v }.inspect
puts "sahip=#{k.method(:transform_values).owner} #{k.method(:sum).owner} #{k.method(:to_h).owner}"
d = [3, 1, 2, 3, nil]
puts d.compact.uniq.sort.inspect
puts [[1, [2]], [3]].flatten.inspect
```
```
{a: 10, b: 20}
{"a" => 1, "b" => 2}
3
[:b, 2]
sahip=Hash Enumerable Hash
[1, 2, 3]
[1, 2, 3]
```

`transform_values` yalnız değerleri çarptı ve anahtarlar korundu; `to_h` bloğu her çifti `[anahtar, değer]` olarak döndürünce anahtarlar dizgiye çevrildi. `sum` değerleri toplayıp `3` verdi, `min_by` `-v` değeri en küçük olan çifti, yani `[:b, 2]`'yi seçti. Sahip satırı ayrımı gösterir: `transform_values` ve `to_h` `Hash`'te, `sum` ise `Enumerable`'dadır. `compact`, `uniq` ve `sort` zinciri `nil`'i ve yinelenen `3`'ü atıp `[1, 2, 3]` verdi; `flatten` iç içe diziyi `[1, 2, 3]` yaptı. **KB38.** Bu yöntemlerin başka alıcılarda aynı sahibi taşıdığı varsayılmaz.

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

Blok her eksiklikte mi yanıtlıyor, yoksa yalnız belirli bir durumda mı? `fetch` yöntemi bunu açıkça ölçer. `fetch` anahtarı arar; bulursa değeri döndürür. Bulamazsa bir blok verilmişse bloğu koşturur, verilmemişse hata fırlatır. Aşağıdaki ölçüm bloğun kaç kez koştuğunu bir sayaçla sayar. Bu sayaç, kaç kez koştuğu sorusunun bir cevabı olarak okunmaz: M10/K03 o soruyu başka bir eksende ölçmüştü, burada sayaç yalnız bloğun hiç çağrılmadığını göstermek içindir.

```ruby
# fetch.rb — blok yalniz anahtar yoksa yanitliyor
cagri = 0
karma = { a: 1 }

var = karma.fetch(:a) { cagri += 1; 0 }
yok = karma.fetch(:b) { |anahtar| cagri += 1; anahtar.to_s }
puts "var=#{var.inspect} yok=#{yok.inspect} cagri=#{cagri}"

bloksuz = begin
  karma.fetch(:b)
rescue KeyError => hata
  hata.class
end
puts "bloksuz=#{bloksuz}"
puts "kose=#{karma[:b].inspect}"
puts "sahip=#{karma.method(:fetch).owner}"
```
```
var=1 yok="b" cagri=1
bloksuz=KeyError
kose=nil
sahip=Hash
```

`fetch` aynı eksik anahtar için üç farklı sonuç verdi. Bloklu çağrı `"b"` döndürdü, bloksuz çağrı `KeyError` fırlattı, köşeli parantez `nil` döndürdü. Anahtar var olduğunda ise sayaç artıran bloklu `fetch(:a)` `1` döndürdü ve sayaç yalnız bir kez arttı: o artış, anahtarı olmayan çağrıya aittir. Yani blok anahtar bulunduğunda hiç çağrılmadı. Bloğun yanıtladığı durum eksik anahtardır; var olan anahtarın değeri hiçbir blok sormadan karma yapının kendisinden gelir.

Bu, ilk bölümdeki yanıtlayanı daraltır. `fetch`'in sahibi `Hash` yani bir sınıf, ama sonucu eksik anahtarda blok belirliyor. Çekirdeğin sınıflandırması bu yüzden blokluyu ayrı bir adımda ölçer: iletinin sahibi her zaman bir sınıf ya da modüldür, soru sonucun kimden geldiğidir. `Hash.new { ... }` gibi `fetch` de varsayılanı çağıranın elinde bırakır; ikisinin farkı, `Hash.new` bloğunun karma yapıya kalıcı olarak bağlanması, `fetch` bloğunun ise yalnız o çağrıya ait olmasıdır.

#### Özet

- Dizi sıralı ve sıfırdan numaralanan bir listedir; karma yapı anahtardan değere giden, eklenme sırasını koruyan bir eşlemedir. Sınır dışı indis ve eksik anahtar `nil` döndürür.
- Ruby 4.0.7'de `size`, `map` (dizide), `each`, `select` ve `fetch` kendi sınıflarında tanımlıdır; `karma.map` ise `Enumerable` modülündedir.
- Bu dersin yanıtlayanı **blok**tur: `Hash.new { 0 }[:x]` `0`, `Hash.new { |_, k| k.to_s }[:x]` `"x"` verdi; iletiyi alan sınıf aynıydı, fark verilen bloktaydı.
- `defined?` bloklu çağrıda `"expression"` döndürür; çekirdek iletiyi bloksuz sorgular.
- Sınırlayıcı ölçüm: blok yalnız anahtar yokken yanıtlar. Sayaç artıran bloklu `fetch(:a)` var olan anahtarda `1` döndürür ve blok hiç koşmaz.

#### Sonraki Adım

Bu derste blok iki karma yapının sonucunu ayırdı, ama blokun kendisi henüz bir konu olarak ele alınmadı: `{ |_, k| k.to_s }` ve `{ 0 }` yalnız yazıldı. Bloklar dersi aynı `map` iletisini iki farklı blokla koşturur ve bir yöntemin, kendisine verilen bloğu nasıl çağırdığını ölçer.

### Bloklar

Source: https://academia.sh/tr/kurslar/ruby-temelleri/bloklar

Aynı map iletisinin iki farklı blokla iki farklı sonuç verdiği, yöntemin bloğu yield ile çağırdığı ve bloğun sonucu belirlerken sınıfın dönüş kuralını değiştirmediği ölçülür.

Önceki ders, `Hash.new { 0 }[:x]` ile `Hash.new { |_, k| k.to_s }[:x]` ifadelerinin iki farklı sonuç verdiğini ve farkı iletiyi alan nesnenin değil, verilen bloğun belirlediğini gösterdi. Bloğun kendisi o derste yalnız yazıldı. Bu ders onu konu yapar: **blok (block)**, süslü parantez ya da `do ... end` ile yazılan ve bir yönteme iletiyle birlikte verilen kod parçasıdır. Blok başlı başına bir değer değildir; yöntem onu çağırdığında koşar. M10/K03, PHP'de bir kapanışın kaç kez koştuğunu ölçmüştü; burada o soru sorulmuyor, soru yine kimin yanıtladığıdır.

Kursun yanıtlayanları **sınıf**, **modül**, **blok** ve **sözdizimi**dir. Bu dersin yanıtlayanı **blok**tur, ama sınırlayıcı ölçüm bloğun her sonucu belirlemediğini gösterecek.

#### Aynı İleti, İki Blok

Aşağıdaki ölçüm aynı dizide aynı `map` iletisini iki farklı bloğa verir. Dizi değişmez, ileti değişmez; yalnız blok değişir. Çekirdek de aynı sonucu sınıflandırır: `yanit.rb` iki sonuç farklıysa yanıtlayanı **blok** sayar.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

```ruby
# blok.rb — ayni map iletisi iki blokla
require_relative "yanit"

sayilar = [1, 2, 3]
iki_kat = sayilar.map { |n| n * 2 }
metin = sayilar.map { |n| n.to_s }

tur, kanit = yanitlayan(defined?(sayilar.map), bloklu: -> { [iki_kat, metin] })
puts "#{tur}  #{kanit}"
puts sayilar.inspect
```
```
blok  [2, 4, 6] / ["1", "2", "3"]
[1, 2, 3]
```

İlk satır çekirdeğin `eleman donusumu` gözlemidir: yanıtlayan **blok**. İkinci satır dizinin değişmediğini gösterir; `map` yeni bir dizi kurdu. **KB3.** Ölçüm dizi üzerindedir; `map` karma yapıda `Enumerable`'dan gelir ve aynı blok mantığıyla çalışır, ama bu ders onu ayrıca ölçmez.

#### Yöntem Bloğu Nasıl Çağırır

Bir yöntem tanımı, çağrıldığında blok verilip verilmediğini sorabilir. `block_given?` bunu doğrudan söyler; yöntem gövdesinde `defined?(yield)` aynı bilgiyi verir: bloklu çağrıda bir dizgi, bloksuz çağrıda `nil`. `yield`, verilen bloğu çağırır ve argümanlarını bloğun parametrelerine aktarır. Üst düzeyde, bir yöntemin dışında `yield` yazılmaz; her ölçüm bir yöntemin içindedir.

```ruby
# yield.rb — yontem blogu yield ile cagirir
def iki_kez
  puts "bloklu=#{block_given?} yield=#{defined?(yield).inspect}"
  return unless block_given?

  yield 1
  yield 2
end

iki_kez
iki_kez { |n| puts "blok #{n}" }
```
```
bloklu=false yield=nil
bloklu=true yield="yield"
blok 1
blok 2
```

Bloksuz çağrıda `block_given?` `false`, `defined?(yield)` `nil` döndü ve yöntem erken çıktı. Bloklu çağrıda ikisi `true` ve `"yield"` oldu; blok `1` ve `2` ile iki kez koştu. **KB4.** `defined?(yield)` `"method"` değil `"yield"` döndürür; `yield` bir yöntem çağrısı değil dilin kendi yapısıdır.

Bloksuz çağrılan `map` ve `each` hata vermez, bir **yineleyici (Enumerator)** döndürür. Bloklu çağrıda `defined?` ise `"expression"` verir; çekirdek bu yüzden iletiyi bloksuz sorgular, daha önce görüldüğü gibi.

```ruby
# bloksuz.rb — bloksuz cagri yineleyici dondurur
puts [1, 2, 3].map.class
puts [1, 2, 3].each.class
puts defined?([1, 2, 3].map)
puts defined?([1, 2, 3].map { |n| n })
```
```
Enumerator
Enumerator
method
expression
```

#### Bloğun Değeri ve Çevresi

`yield` bir ifadedir ve bloğun son ifadesinin değerini döndürür. Yöntem bu değeri kullanabilir; `map` tam olarak bunu yapar. Blok ayrıca tanımlandığı yerdeki yerel değişkenleri görür: bu, M01/K02'de anlatılan **kapanış (closure)** davranışıdır ve burada yeniden tanımlanmaz, yalnız ölçülür.

```ruby
# deger.rb — yield blogun degerini dondurur, blok cevresini gorur
def uc_kat
  sonuc = yield 3
  "yield dondu: #{sonuc}"
end

carpan = 5
puts uc_kat { |n| n * carpan }
carpan = 7
puts uc_kat { |n| n * carpan }

toplam = 0
[1, 2, 3].each { |n| toplam += n }
puts "toplam=#{toplam}"
```
```
yield dondu: 15
yield dondu: 21
toplam=6
```

İki çağrıda blok aynı yazılmıştı, ama `carpan` değişkeninin o anki değerini gördü: önce `15`, sonra `21`. Blok tanımlandığında değeri kopyalamadı; çağrıldığı anda değişkenin o anki değerini okudu. `each` bloğu içinde `toplam` dışarıdaki değişkeni artırdı. **KB6.** Bu ders yalnız bloğun tanımlandığı kapsamdaki değişkenleri gördüğünü ölçer, başka kapsam kurallarını ölçmez.

#### İki Yazım, Çok Parametre

Süslü parantez tek satırlık bloklarda, `do ... end` çok satırlı bloklarda tercih edilir; ikisi aynı bloğu kurar. Blok birden çok parametre alabilir ve `yield` kaç değer verirse parametreler onlara sırayla bağlanır.

```ruby
# yazim.rb — iki yazim ayni blogu kurar, cok parametre
def cift
  yield 1, 2
end

kisa = cift { |a, b| a + b }
uzun = cift do |a, b|
  a + b
end
puts "kisa=#{kisa} uzun=#{uzun}"

{ ad: "Ayse", yas: 30 }.each do |anahtar, deger|
  puts "#{anahtar}=#{deger}"
end
```
```
kisa=3 uzun=3
ad=Ayse
yas=30
```

İki yazım da `3` verdi. Karma yapının `each` bloğu anahtarı ve değeri iki ayrı parametre olarak aldı. **KB7.** İki yazımın yalnız bu çağrılarda aynı sonucu verdiği ölçüldü; parantezsiz çağrıda bloğun hangi yönteme bağlandığı Hangi Yönteme Bağlanır bölümünde ayrıca ölçülür.

#### Kısa Biçimler

Ruby 3.4'ten beri tek parametreli bir blokta `it` yazılabilir; `_1` ise Ruby 2.7'den beri vardır. İkisi de aynı sonucu verir.

```ruby
# kisa.rb — tek parametreli blokta it ve _1
puts [1, 2].map { it * 3 }.inspect
puts [1, 2].map { _1 * 3 }.inspect
```
```
[3, 6]
[3, 6]
```

#### Parametre Açma ve Sıra

Blok birden çok parametre alırsa, yöntemin ilettiği tek bir dizi parametrelere dağıtılır. `|a, b|` bir çifti açar, `|a, (b, c)|` iç içe diziyi de açar. `each_with_index` ise elemanla birlikte sırayı da iletir.

```ruby
# acma.rb — blok parametrelerinde dizi acma
[[1, 2], [3, 4]].each { |a, b| puts "#{a}+#{b}=#{a + b}" }
%w[x y].each_with_index { |harf, sira| puts "#{sira}:#{harf}" }
[[1, [2, 3]]].each { |a, (b, c)| puts "a=#{a} b=#{b} c=#{c}" }
puts [[1, 2], [3, 4]].map { |cift| cift }.inspect
```
```
1+2=3
3+4=7
0:x
1:y
a=1 b=2 c=3
[[1, 2], [3, 4]]
```

`[[1, 2], [3, 4]]` üzerinde `|a, b|` her çifti iki parametreye böldü: `1+2=3` ve `3+4=7`. `each_with_index` elemanı ve sırayı ayrı iki parametre olarak verdi. Parantezli `(b, c)` iç diziyi açtı. Tek parametreli `map { |cift| cift }` ise diziyi bölmeden olduğu gibi aldı. **KB23.** Açma yalnız dizi elemanlarında ve bu dört çağrıda ölçüldü.

#### Bloğun Dış Yereli

Blok, tanımlandığı yerdeki yerel değişkenleri görür ve değiştirebilir. Bu, yukarıda ölçülen kapanış davranışıdır. Blok parametresi dıştaki bir değişkenle aynı adı taşırsa ne olduğu da aşağıda ölçülür; `|x; y|` yazımı ise noktalı virgülden sonraki adı bloğa yerel yapar.

```ruby
# dis_yerel.rb — blok dis degiskeni gorur ve degistirir
toplam = 0
[1, 2, 3].each { |n| toplam += n }
puts toplam
n = 100
[1, 2].each { |n| n }
puts n
y = 1
[5].each { |x; y| y = x }
puts y
```
```
6
100
1
```

`toplam` bloğun dışında tanımlıydı ve blok her turda onu artırdı: sonuç `6`. İkinci örnekte blok parametresi `n`, dıştaki `n` ile aynı ada sahipti; blok içindeki `n` ayrı bir değişken oldu ve dıştaki `n` `100` olarak kaldı. Üçüncüde `|x; y|` ile bloğa yerel bir `y` tanımlandı; blok `y = x` yazsa da dıştaki `y` `1` kaldı. Blok dıştaki yereli değiştirebilir; bu değiştirme yalnız ad bloğa yerel olarak bildirilmediyse olur. **KB24.**

#### Hangi Yönteme Bağlanır

İki blok yazımı bu dersin başında aynı sonucu verdi. Fark parantezsiz çağrıda ortaya çıkar: süslü parantez en yakın yönteme bağlanır, `do` ve `end` ise aşağıdaki ölçümde dıştaki yönteme bağlandı. Aşağıdaki `sor` yöntemi yalnız kendisine blok verilip verilmediğini bildirir.

```ruby
# oncelik.rb — blogun hangi yonteme baglandigi
def sor(*) = "blok=#{block_given?}"
puts sor(1) { }
puts sor 1 do end
```
```
blok=true
blok=false
```

`sor(1) { }` bloğu `sor`'a verdi ve `blok=true` basıldı. `puts sor 1 do end` bloğu `puts`'a bağladı; `sor` blok almadı ve `blok=false` basıldı. Parantezsiz çağrıda `do` ve `end` bloğu dıştaki yönteme götürür. **KB25.** Yalnız bu iki çağrı ölçüldü; zincirleme çağrılardaki bağlanma ayrıca ölçülmedi.

#### Bloktan Değer Döndürmek

Blok içinde `next`, bloğu o turda bitirir ve verilen değeri bloğun değeri yapar. `break` ise yöntemin kendisini bitirir ve verilen değer yöntem çağrısının sonucu olur.

```ruby
# sonraki.rb — next ve break ile deger
puts [1, 2, 3, 4].map { |n| next 0 if n.even?; n * 10 }.inspect
puts [1, 2, 3].each { |n| break n * 5 if n == 2 }
```
```
[10, 0, 30, 0]
10
```

`next 0 if n.even?` çift sayılarda bloğun değerini `0` yaptı ve `map` `[10, 0, 30, 0]` döndürdü. `each` içinde `break n * 5`, `n == 2` olduğunda döngüyü kesti ve `each` çağrısının değeri `10` oldu. **KB26.** `next` ve `break` yalnız `map` ve `each` ile ölçüldü.

#### Esnek Parametre Sayısı

Blok, yöntemin ilettiği değer sayısıyla parametre sayısının eşit olmasını şart koşmaz. Fazla iletilen değerler atılır, eksik kalan parametre `nil` olur. Tek bir dizi iletildiğinde ise birden çok parametre onu açar.

```ruby
# esnek.rb — blok parametre sayisi ile iletilen deger sayisi
def iki
  yield 1, 2
end
iki { |a| puts "a=#{a.inspect}" }
iki { |a, b, c| puts "c=#{c.inspect}" }
def tek
  yield [1, 2]
end
tek { |a, b| puts "a=#{a} b=#{b}" }
tek { |a| puts "a=#{a.inspect}" }
```
```
a=1
c=nil
a=1 b=2
a=[1, 2]
```

`yield 1, 2` iki değer iletti. Tek parametreli blok yalnız ilkini aldı, üç parametreli blokta üçüncü `nil` kaldı ve hata çıkmadı. `yield [1, 2]` tek bir dizi iletti: iki parametreli blok onu açtı, tek parametreli blok diziyi bütün olarak aldı. Bu gevşeklik blokta ve proc'ta görülür; Proc ve Lambda dersi aynı sınamayı lambda ile yapar. **KB39.**

#### Bloğun Değerini Yöntemde Kullanmak

`yield` bir ifadedir ve değeri bloğun son ifadesidir. Yöntem bu değeri biriktirebilir ya da bir koşul olarak kullanabilir; ne yapılacağını bu durumda yöntem değil, verilen blok belirler.

```ruby
# yield_deger.rb — yield ifadesinin degeri
def donustur(liste)
  sonuc = []
  liste.each { |x| sonuc << yield(x) }
  sonuc
end
puts donustur([1, 2, 3]) { |x| x * 2 }.inspect
def say(liste) = liste.count { |x| yield x }
puts say([1, 2, 3, 4]) { |x| x.even? }
```
```
[2, 4, 6]
2
```

`donustur` her `yield(x)` değerini sonuca ekledi ve `[2, 4, 6]` verdi. `say` bloğun doğru döndürdüğü elemanları saydı ve `2` verdi. İki yöntemde de sonucu blok belirledi; `donustur` iki katına almayı, `say` da çift sayıyı seçmeyi bilmiyor; ikisi yalnız bloğun değerini kullanıyor. Yanıtlayan bu yüzden bloktur. **KB40.**

#### Blok Parametresi ve Sembol

Bir yöntem `&blok` parametresi tanımlarsa, verilen bloğu bir nesne olarak alır; blok verilmediyse bu parametre `nil` olur. `&:ad` yazımı ise bir sembolü blok yerine geçirir.

```ruby
# blok_nesnesi.rb — &blok parametresi ve sembol
def al(&b) = b
puts al.inspect
puts al { }.class
puts %w[a b].map(&:upcase).inspect
puts :upcase.to_proc.call("x")
```
```
nil
Proc
["A", "B"]
X
```

`al` bloksuz çağrılınca `nil`, bloklu çağrılınca bir `Proc` döndürdü. `map(&:upcase)` her elemana `upcase` iletisini gönderdi ve `["A", "B"]` verdi; `:upcase.to_proc.call("x")` da `X` döndürdü. Sembolün kendisi blok değildir, `to_proc` ile bir `Proc`'a dönüşür. **KB41.** Proc nesneleri Proc ve Lambda dersinde ayrıntılanır.

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

Blok her zaman sonucu belirler mi? `each` iletisi bloğu her eleman için koşturur, ama dönüş değeri bloğun sonucu değildir. İki farklı blok aynı dönüş değerini verir ve yanıtlayan blok değil, `each`'in sahibi olan sınıftır.

```ruby
# donus.rb — each icin blok sonucu donusu degistirmez
sayilar = [1, 2]
a = sayilar.each { |n| n * 10 }
b = sayilar.each { |n| n * 99 }
puts a.inspect
puts b.inspect
puts "sahip=#{sayilar.method(:each).owner}"
```
```
[1, 2]
[1, 2]
sahip=Array
```

İki blok farklı değerler hesapladı, ama ikisi de `each`'ten `[1, 2]` döndü. Dönüş değerini bloklar değil, `Array` sınıfındaki `each` tanımı belirliyor. Yani `map` için blok sonucu belirler, `each` için belirlemez; aynı blok sözdizimi iki iletide iki farklı iş görür. **KB5.** Bu fark yalnız bu iki yöntemde ölçüldü, diğer yineleme yöntemlerine genellenmez.

#### Özet

- Blok, bir yönteme iletiyle verilen kod parçasıdır; yöntem onu `yield` ile çağırır.
- Bu dersin yanıtlayanı **blok**tur: aynı `map` iletisi `{ |n| n * 2 }` ile `[2, 4, 6]`, `{ |n| n.to_s }` ile `["1", "2", "3"]` verdi.
- `block_given?` ve `defined?(yield)` bloğun varlığını sorar; bloksuz `map` ve `each` bir yineleyici döndürür.
- `it` Ruby 3.4'ten beri, `_1` Ruby 2.7'den beri tek parametreli blokta kullanılır.
- Sınırlayıcı ölçüm: `each` iki farklı blokla da `[1, 2]` döndürür; dönüş değerini bu durumda `Array` sınıfı belirler.

#### Sonraki Adım

`map` dizide `Array`'in, karma yapıda `Enumerable`'ın yöntemiydi. Sonraki ders bu modülün kendisine bakar: dizinin kendisinde olmayan bir yöntem nereden geliyor ve kendi sınıfınıza aynı yetenek tek bir `each` ile nasıl verilir.

### `Enumerable` Modülü

Source: https://academia.sh/tr/kurslar/ruby-temelleri/enumerable-modulu

Dizinin kendisinde olmayan yineleme yöntemlerinin Enumerable modülünden geldiği, kendi sınıfınıza tek bir each ile aynı yeteneğin verildiği ve modülün her işi üstlenmediği ölçülür.

Önceki derste `karma.map` yönteminin `Hash`'te değil `Enumerable` modülünde tanımlı olduğu görüldü. Bu ders o modüle bakar. **Modül (module)**, yöntemlerin bir sınıfa **karıştırıldığı (include)** bir yöntem topluluğudur; kendi başına nesne üretmez. M08'de nesne yönelimli temeller kuruldu; burada yalnız bu modülün hangi iletileri yanıtladığı ölçülür.

Bu dersin yanıtlayanı **modül**dür. Dizi, aralık ve karma yapı farklı sınıflardır, ama birçok yineleme iletisini ortak bir modül yanıtlar.

#### Modülün Yanıtladığı İletiler

İlk ölçüm bir aralığa `each_slice` iletisini gönderir. Çekirdek sonucu, yöntemin sahibine bakarak sınıflandırır: sahip bir `Class` ise yanıtlayan sınıf, değilse modüldür.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

```ruby
# dilim.rb — each_slice iletisini kim yanitliyor
require_relative "yanit"

aralik = (1..5)
sahip = aralik.method(:each_slice).owner
sonuc = aralik.each_slice(2).to_a
tur, kanit = yanitlayan(defined?(aralik.each_slice(2)), sahip: sahip, sonuc: sonuc)
puts "#{tur}  #{kanit}"
```
```
modul  Enumerable, sonuc [[1, 2], [3, 4], [5]]
```

Bu, çekirdeğin `dilimleme` satırıdır: yanıtlayan **modül**, sahip `Enumerable`. `Range` sınıfı `each_slice` yöntemini kendisi tanımlamıyor; aralığa `each` ile elemanlarını vermeyi bilmesi yeterli. Aynı modülün diğer yöntemleri bu dersin ikinci ölçümündedir.

```ruby
# araclar.rb — Enumerable yontemleri
meyveler = %w[elma armut kiraz muz]
puts meyveler.sort_by { |m| m.size }.first
puts meyveler.group_by { |m| m.size }.inspect
puts meyveler.min_by { |m| m.size }
puts %w[a b a].tally.inspect
puts (1..6).select(&:even?).inspect
```
```
muz
{4 => ["elma"], 5 => ["armut", "kiraz"], 3 => ["muz"]}
muz
{"a" => 2, "b" => 1}
[2, 4, 6]
```

`sort_by` en kısa adı başa aldı, `group_by` adları uzunluğa göre gruplayan bir karma yapı kurdu, `min_by` en kısa adı verdi. `tally` her elemanın kaç kez geçtiğini saydı. `select(&:even?)` aralıktan çift sayıları seçti.

#### Kendi Sınıfınıza Karıştırmak

Modülün yeteneği ancak bir sınıfa karıştırıldığında bir sınıfın olur. En küçük biçimi: sınıf yalnız `each` tanımlar ve `include Enumerable` der. Sınıf tasarımı M08'in konusudur; burada sınıf yalnız ölçüm için en küçük hâlinde kurulur.

```ruby
# sayilar.rb — kendi sinifimiza Enumerable karistirmak
class Sayilar
  include Enumerable

  def each
    yield 3
    yield 1
    yield 2
  end
end

sayilar = Sayilar.new
puts sayilar.map { |n| n * 2 }.inspect
puts sayilar.sort.inspect
puts sayilar.min_by { |n| -n }
puts "map=#{sayilar.method(:map).owner} each=#{sayilar.method(:each).owner}"
puts sayilar.to_a.inspect
puts sayilar.include?(2)
puts sayilar.first
puts sayilar.reduce(:+)
puts sayilar.each_with_index.to_a.inspect
puts sayilar.is_a?(Enumerable)
```
```
[6, 2, 4]
[1, 2, 3]
3
map=Enumerable each=Sayilar
[3, 1, 2]
true
3
6
[[3, 0], [1, 1], [2, 2]]
true
```

Sınıf yalnız `each` yazdı; `map`, `sort` ve `min_by` ek tanım yazmadan geldi. `min_by { -n }` en büyük sayıyı verdi, çünkü eksi değeri en küçük olan `3`'tür. Son satır paylaşımı gösterir: `map`'in sahibi `Enumerable`, `each`'in sahibi `Sayilar` sınıfı. `to_a` elemanları `each`'in verdiği sırayla topladı: `[3, 1, 2]`. `include?`, `first` ve `reduce` de aynı tek `each`'e dayandı; `reduce(:+)` elemanları toplayıp `6` verdi. `each_with_index` her elemana sıra numarasını ekledi. Son satır, sınıfın örneklerinin `Enumerable` olarak tanındığını doğrular. Sınıf `<=>` tanımlamadı, çünkü `sort` burada sayıları karşılaştırırken sayıların kendi karşılaştırmasını kullanır; sınıfın örneklerini kendi aralarında karşılaştırmak ayrı bir iştir. **KB8.** Bu sınıf `each` ile üç değer verir; `each` sonsuz bir dizi üretirse `sort` gibi tümünü okuyan yöntemler hiç bitmez, ama bu davranış burada ölçülmez.

#### Modülün Döndürdüğü Türler

Modülün yöntemleri alıcının türünden bağımsız olarak belirli türler döndürür. Bu, modülün kullanıcıya verdiği bir sözleşmedir: aralığa, diziye ya da kendi sınıfınıza aynı iletiyi gönderdiğinizde sonuç türünü alıcıya bakmadan bilirsiniz.

```ruby
# turler.rb — Enumerable yontemlerinin dondurdugu turler
aralik = (1..3)
puts aralik.map { |n| n }.class
puts aralik.select { |n| n > 1 }.class
puts aralik.group_by { |n| n.odd? }.class
puts aralik.tally.class
puts aralik.each_slice(2).class
puts aralik.min_by { |n| -n }.class
```
```
Array
Array
Hash
Hash
Enumerator
Integer
```

`map` ve `select` dizi, `group_by` ve `tally` karma yapı döndürdü; bloksuz `each_slice` bir yineleyici verdi; `min_by` tek bir eleman verdi. Aralık burada alıcıydı, ama sonuç türleri aralığa değil modülün tanımına bağlı. Diziler ve Karma Yapılar dersinin `Hash#select` örneği bunun sınırını gösterir: orada sınıfın kendi tanımı karma yapı döndürmüştü. **KB9.** Bu tablo yalnız aralık alıcısıyla ölçüldü; aynı yöntemlerin kendi sınıfınızda aynı türleri döndürdüğü yukarıdaki `Sayilar` ölçümünden `map` için görülmüştür, diğerleri için varsayılmaz.

#### Zincirleme Yöntemler

`Enumerable`'ın asıl gücü, küçük yöntemlerin birbirine bağlanmasıdır. Aşağıdaki ölçüm beş yöntemi bir aralık üzerinde koşturur ve her birinin sahibini yanına basar.

```ruby
# zincir.rb — her yontemin sonucu ve sahibi
s = (1..4)
puts "each_with_object=#{s.each_with_object([]) { |n, a| a << n * n }.inspect} sahip=#{s.method(:each_with_object).owner}"
puts "inject=#{s.inject(:+)} reduce=#{s.reduce(1) { |a, n| a * n }} sahip=#{s.method(:inject).owner}"
puts "zip=#{s.zip(%w[a b c d]).inspect} sahip=#{s.method(:zip).owner}"
puts "partition=#{s.partition(&:even?).inspect} sahip=#{s.method(:partition).owner}"
puts "each_cons=#{s.each_cons(2).to_a.inspect} sahip=#{s.method(:each_cons).owner}"
```
```
each_with_object=[1, 4, 9, 16] sahip=Enumerable
inject=10 reduce=24 sahip=Enumerable
zip=[[1, "a"], [2, "b"], [3, "c"], [4, "d"]] sahip=Enumerable
partition=[[2, 4], [1, 3]] sahip=Enumerable
each_cons=[[1, 2], [2, 3], [3, 4]] sahip=Enumerable
```

`each_with_object` boş bir diziyi taşıyarak kareleri topladı. `inject(:+)` toplamı, `reduce(1) { ... }` çarpımı verdi; `inject`'in sahibi `Enumerable` olarak basıldı. `zip` iki diziyi çiftledi, `partition` blok doğru olanları ve olmayanları iki ayrı diziye ayırdı, `each_cons(2)` ardışık ikilileri verdi. Beş yöntemin beşinde de basılan sahip `Enumerable` oldu: `Range` kendi `each`'ini tanımlasa da bu zincirin yanıtlayanı modüldür. **KB27.** Sahip ölçümü yalnız `Range` alıcısıyla yapıldı; `reduce`'un sahibi ayrıca basılmadı.

#### Tembel Zincir

`lazy` zinciri hemen çalıştırmaz; sonucu istenen kadar elemanı üretir. Sonsuz bir aralık üzerinde `select` ve `first(3)` bu yüzden tamamlanabilir. `with_index` ise elemanlara sıra ekler ve ilk sıra numarası verilebilir.

```ruby
# tembel.rb — lazy zincir ve with_index
puts (1..Float::INFINITY).lazy.select(&:even?).first(3).inspect
puts (1..3).lazy.class
puts "lazy sahip=#{(1..3).method(:lazy).owner}"
puts %w[a b].each.with_index(1).to_a.inspect
puts %w[a b].map.with_index { |h, i| "#{i}#{h}" }.inspect
```
```
[2, 4, 6]
Enumerator::Lazy
lazy sahip=Enumerable
[["a", 1], ["b", 2]]
["0a", "1b"]
```

Sonsuz aralıkta `first(3)` ilk üç çift sayıyı bulunca durdu ve `[2, 4, 6]` verdi. `lazy` iletisini `Enumerable` yanıtladı ve dönen nesnenin sınıfı `Enumerator::Lazy` oldu. `each.with_index(1)` sırayı birden başlattı; `map.with_index` ise bloğa elemanla birlikte sırayı verdi ve dönüştürülmüş diziyi üretti. **KB28.** Yalnız bu üç zincir ölçüldü.

#### Dışarıdan Yineleyici

Bloksuz çağrılan `each` bir yineleyici döndürür; `next` onu elle ilerletir. Elemanlar bittiğinde `StopIteration` fırlatılır.

```ruby
# dis_yineleyici.rb — next ile elle ilerletme
e = [1, 2, 3].each
puts e.next
puts e.next
puts e.next
sonuc = begin
  e.next
rescue StopIteration
  "bitti"
end
puts sonuc
puts e.size
```
```
1
2
3
bitti
3
```

Üç `next` çağrısı elemanları sırayla verdi. Dördüncü çağrı `StopIteration` fırlattı, `rescue` onu yakaladı ve `bitti` basıldı. `size` `3` döndürdü. **KB29.** `size` yalnız bu örnekte ölçüldü; boyutu önceden bilinmeyen yineleyiciler ölçülmedi.

#### Karışımdan Gelen Yöntemler

Kendi sınıfınız yalnız `each` tanımlayıp `Enumerable`'ı karıştırınca onlarca yöntem kazanır. Aşağıdaki `Kutu` sınıfı elemanlarını bir dizide tutar ve `each` içinde dizinin `each` yöntemine devreder. Ardından dokuz farklı yöntem çağrılır.

```ruby
# kutu.rb — yalniz each tanimlayan sinif
class Kutu
  include Enumerable

  def initialize(*elemanlar)
    @elemanlar = elemanlar
  end

  def each(&blok)
    @elemanlar.each(&blok)
    self
  end
end

k = Kutu.new(3, 1, 2)
puts "sort=#{k.sort.inspect} sahip=#{k.method(:sort).owner}"
puts "min=#{k.min} max=#{k.max} sum=#{k.sum}"
puts "map=#{k.map { |n| n * 2 }.inspect}"
puts "each_slice=#{k.each_slice(2).to_a.inspect}"
puts "first=#{k.first} count=#{k.count} sort_by=#{k.sort_by { |n| -n }.inspect}"
puts "each sahip=#{k.method(:each).owner}"
```
```
sort=[1, 2, 3] sahip=Enumerable
min=1 max=3 sum=6
map=[6, 2, 4]
each_slice=[[3, 1], [2]]
first=3 count=3 sort_by=[3, 2, 1]
each sahip=Kutu
```

`Kutu` yalnız `each` tanımladı; `sort`'un sahibi `Enumerable` olarak basıldı. `min`, `max`, `sum`, `map`, `each_slice`, `first`, `count` ve `sort_by` hep `each` üzerinden çalıştı ve beklenen sonuçları verdi. Yanıtlayan modüldür; `each`'in sahibi ise `Kutu`'dur. Modül, elemanlara nasıl ulaşılacağını sınıftan öğrenir. **KB42.** Yalnız bu dokuz yöntem denendi; karışımın her yönteminin çalıştığı varsayılmaz.

#### each Olmayınca

Modül `each`'i kendisi sağlamaz, sınıfın tanımlaması gerekir. `each` tanımlamayan bir sınıf karışımı alır ama yöntemler çalışmaz.

```ruby
# bos.rb — each tanimlamayan sinif
class Bos
  include Enumerable
end

sonuc = begin
  Bos.new.map { |x| x }
rescue NoMethodError => hata
  hata.class
end
puts sonuc
puts Bos.new.respond_to?(:each)
```
```
NoMethodError
false
```

`Bos` sınıfı `Enumerable`'ı karıştırdı ama `each` tanımlamadı. `map` çağrısı `NoMethodError` fırlattı ve `respond_to?(:each)` `false` verdi. Sözleşme tek yönlüdür: `Enumerable` yöntemleri, sınıfın `each` yöntemini çağırabileceğini varsayar. **KB43.** Hata iletisi basılmadı; yalnız sınıfı ölçüldü.

#### Sorgu Yöntemleri ve Sahipleri

`any?`, `all?`, `none?`, `find` ve `take_while` koleksiyonu sorgular. Bir dizide bu yöntemlerin hepsi `Enumerable`'dan mı gelir? Sahip satırı bunu ölçer.

```ruby
# sorgular.rb — sorgu yontemleri ve sahipleri
s = [1, 2, 3, 4]
puts "any=#{s.any? { |n| n > 3 }} all=#{s.all?(Integer)} none=#{s.none?(&:zero?)}"
puts "find=#{s.find { |n| n > 2 }} take_while=#{s.take_while { |n| n < 3 }.inspect}"
puts "sahip=#{s.method(:take_while).owner} #{s.method(:find).owner} #{s.method(:any?).owner}"
```
```
any=true all=true none=true
find=3 take_while=[1, 2]
sahip=Array Array Array
```

Sorgu sonuçları beklenen çıktı: `any?`, `all?(Integer)` ve `none?(&:zero?)` doğru, `find` `3`, `take_while` `[1, 2]` verdi. Üç yöntemin sahibi de `Array` olarak basıldı: dizi bu yöntemleri `Enumerable`'a bırakmadan kendi tanımlıyor. Önceki bölümde `Kutu` için `sort`'un sahibi `Enumerable`'dı; yani yanıtlayan alıcının sınıfına bağlıdır. **KB44.** Ölçüm Ruby 4.0.7'de ve yalnız `Array` alıcısıyla yapıldı.

#### Karışımın Başka Yöntemleri

Karışımla gelen yöntemler yalnız toplama ve sıralamayla sınırlı değildir. En küçük ve en büyüğü birlikte veren `minmax`, ilk `n` küçüğü veren `min(2)`, sırayı da ileten `each_with_index`, tembel zincir ve tersten gezme de `each`'e dayanır. Aşağıdaki `Raf` sınıfı yine yalnız `each` tanımlar.

```ruby
# raf.rb — each tanimlayan ikinci sinif ve ek yontemler
class Raf
  include Enumerable

  def initialize(*elemanlar)
    @elemanlar = elemanlar
  end

  def each(&blok) = @elemanlar.each(&blok)
end

r = Raf.new(4, 2, 9)
puts r.minmax.inspect
puts r.min(2).inspect
puts r.each_with_index.map { |n, i| n * i }.inspect
puts r.lazy.map { |n| n + 1 }.first(2).inspect
puts r.entries.inspect
puts r.reverse_each.to_a.inspect
```
```
[2, 9]
[2, 4]
[0, 2, 18]
[5, 3]
[4, 2, 9]
[9, 2, 4]
```

`minmax` `[2, 9]`, `min(2)` `[2, 4]` verdi. `each_with_index.map` her elemanı sırasıyla çarptı: `[0, 2, 18]`. `lazy.map { ... }.first(2)` yalnız ilk iki elemanı dönüştürdü: `[5, 3]`. `entries` elemanları ekleniş sırasıyla, `reverse_each` tersine verdi. Altı çağrının hiçbirinde `Raf` yeni bir yöntem tanımlamadı; hepsi `each`'ten türedi. **KB52.** Bu yöntemlerin sahibi basılmadı; yalnız sonuçları ölçüldü.

#### Sıralama İçin Comparable

`sort` elemanları `<=>` ile karşılaştırır. Bu karşılaştırmayı bilmeyen nesneler sıralanamaz. `<=>` tanımlayıp `Comparable`'ı karıştıran bir sınıf ise hem `sort`'a hem `min`'e hem de `<` gibi işleçlere kavuşur.

```ruby
# karsilastir.rb — comparable ve sort
class Nesne; end
sonuc = begin
  [Nesne.new, Nesne.new].sort
rescue ArgumentError => hata
  hata.class
end
puts sonuc

class Etiket
  include Comparable
  attr_reader :boy

  def initialize(boy)
    @boy = boy
  end

  def <=>(diger) = boy <=> diger.boy
end

puts [Etiket.new(3), Etiket.new(1)].sort.map(&:boy).inspect
puts [Etiket.new(3), Etiket.new(1)].min.boy
puts Etiket.new(1) < Etiket.new(2)
puts "sahip=#{Etiket.new(1).method(:<).owner}"
```
```
ArgumentError
[1, 3]
1
true
sahip=Comparable
```

`<=>` tanımı olmayan iki `Nesne` sıralanamadı: `sort` `ArgumentError` fırlattı. `Etiket` yalnız `<=>` tanımladı ve `Comparable`'ı karıştırdı; `sort` `[1, 3]`, `min` `1` verdi. `<` işleci de çalıştı ve `<`'ün sahibi `Comparable` olarak basıldı. İşleci yanıtlayan modüldür; sınıf yalnız karşılaştırma kuralını verdi. **KB53.** Ölçüm tek bir alanla yapıldı.

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

Modül her işi üstlenmez. Aynı adlı yöntem farklı alıcılarda farklı sahipten gelebilir: sınıf kendi tanımını yazdıysa o yanıtlar.

```ruby
# sahipler.rb — ayni ad, farkli sahipler
puts "(1..3).sum   #{(1..3).method(:sum).owner}"
puts "[3, 1].sum   #{[3, 1].method(:sum).owner}"
puts "[1].sort_by  #{[1].method(:sort_by).owner}"
puts "[3, 1].sort  #{[3, 1].method(:sort).owner}"
puts "[3, 1].min   #{[3, 1].method(:min).owner}"
puts "(1..3).min   #{(1..3).method(:min).owner}"
```
```
(1..3).sum   Enumerable
[3, 1].sum   Array
[1].sort_by  Enumerable
[3, 1].sort  Array
[3, 1].min   Array
(1..3).min   Range
```

`sum` aralıkta `Enumerable`'dan, dizide `Array`'den geliyor. `sort_by` dizide de `Enumerable`'a ait; ama `sort` ve `min` dizide `Array`'in kendi yöntemleri. Aralık için `min` ise `Range`'in kendi yöntemi. Sonuçlar Ruby 4.0.7'ye aittir. Altı satır, üç ayrı sahip verdi: `Enumerable`, `Array` ve `Range`. `sum` ve `min` adları her iki alıcıda da çalışır, çağıran taraf farkı görmez; ama iletiyi alıcıya göre bir sınıf ya da modül yanıtlar. Bu yüzden `owner` sorusu, yöntemin adından değil alıcının türünden başlar. **KB10.** Altı satırın hiçbiri sonucun değerini karşılaştırmaz; yalnız sahip sorulur, yani bu ölçüm modülün ve sınıfın aynı sonucu verip vermediğini söylemez. Yani aynı ad, alıcıya göre modülün ya da sınıfın yanıtıdır; bunun nedeni sonraki kursun konusudur ve burada açıklanmaz.

#### Özet

- `Enumerable`, `each` tanımlayan her sınıfa `map`, `sort`, `min_by`, `group_by` ve `tally` gibi yöntemler kazandıran bir modüldür.
- Bu dersin yanıtlayanı **modül**dür: `(1..5).each_slice(2)` iletisini `Range` değil `Enumerable` yanıtlar.
- Kendi sınıfına `include Enumerable` ve tek bir `each` eklemek yeterlidir; `map`'in sahibi modül, `each`'in sahibi sınıf çıkar.
- Sınırlayıcı ölçüm: aynı ad alıcıya göre farklı sahipten gelir; `sum` aralıkta modülden, dizide `Array`'den gelir.

#### Sonraki Adım

Bu derste bir yöntemin gövdesi `yield` ile bloğu çağırdı. Sonraki ders bloğu bir nesneye dönüştürür: `Proc` ve lambda, aynı kodu bir değişkende tutmanın iki yoludur ve `call` iletisinin sahibi bu iki yol için aynıdır.

### Proc ve Lambda

Source: https://academia.sh/tr/kurslar/ruby-temelleri/proc-ve-lambda

Bir bloğun nesneye dönüştürülmesinin iki yolu olan proc ve lambda için call iletisini bir sınıfın yanıtladığı, farkın argüman denetiminde ve lambda? durumunda yattığı ölçülür.

Önceki iki derste blok yalnız bir yöntemin çağrısına eşlik eden kod parçasıydı: yazıldığı yerde kaldı, bir değişkende doğrudan saklanamadı. **Proc**, bloğu bir nesneye dönüştürür; **lambda** ise aynı nesnenin daha kati davranan biçimidir. İkisinin de sınıfı `Proc`'tur. Bir proc, tanımlandığı kapsamdaki değişkenleri taşır; bu özelliğe M01/K02'de **kapanış (closure)** denmişti ve burada yeniden tanımlanmaz.

Bu dersin yanıtlayanı **sınıf**tır: `call` iletisini `Proc` sınıfı yanıtlar. Fark ise sınıfta değil, nesnenin **lambda? durumu**ndadır.

#### Çağırmanın Üç Yolu

Lambda `->` ile yazılır. Çağırmak için `call`, nokta-parantez kısayolu `.()` ya da köşeli parantez kullanılır; üçü aynı iletidir. Çekirdek bu çağrıyı sınıflandırır: `call`'ın sahibi bir `Class` ise yanıtlayan **sınıf**tır.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

```ruby
# lambda.rb — lambda cagrisini kim yanitliyor
require_relative "yanit"

iki_kat = ->(n) { n * 2 }
sahip = iki_kat.method(:call).owner
tur, kanit = yanitlayan(defined?(iki_kat.call(3)), sahip: sahip, sonuc: iki_kat.call(3))
puts "#{tur}  #{kanit}"
puts iki_kat.class
puts iki_kat.call(3)
puts iki_kat.(3)
puts iki_kat[3]
```
```
sinif  Proc, sonuc 6
Proc
6
6
6
```

İlk satır çekirdeğin `lambda cagrisi` gözlemidir: yanıtlayan **sınıf**, sahip `Proc`. Üç çağrı biçimi de `6` verdi. **KB11.** Üç biçim bu derste aynı sonuçla ölçüldü; hangisinin tercih edildiği bir yazım tercihidir ve ölçülmez.

#### Proc ile Lambda Arasındaki Fark

Fark ilk bakışta görünmez: ikisi de çağrılır, ikisi de değer döndürür. Ayrım argüman denetiminde ortaya çıkar. Lambda tam sayıda argüman bekler; proc ise fazlayı yok sayar, eksiği `nil` ile doldurur. Hata durumunda lambda'nın fırlattığı istisnanın sınıfı basılır, iletisi değil.

```ruby
# fark.rb — proc ve lambda arasindaki arguman farki
gevsek = proc { |x| x }
kati = lambda { |x| x }
puts gevsek.call(1, 2)

sonuc = begin
  kati.call(1, 2)
rescue ArgumentError => hata
  hata.class
end
puts sonuc

cift = proc { |x, y| [x, y] }
puts cift.call(1).inspect
puts "gevsek=#{gevsek.lambda?} kati=#{kati.lambda?}"
```
```
1
ArgumentError
[1, nil]
gevsek=false kati=true
```

Proc iki argümanı birden verildiğinde fazlayı bıraktı ve `1` döndürdü; lambda aynı çağrıda `ArgumentError` fırlattı. İki parametreli proc'a tek argüman verilince eksik parametre `nil` oldu. `lambda?` ikisini ayırır: proc için `false`, lambda için `true`.

#### Başka Nesnelerden Proc

Ruby'de başka kaynaklardan da proc elde edilir: bir yöntem nesnesinden `to_proc`, bir sembolden `to_proc`. İkisi de lambda gibi davranır.

```ruby
# donusum.rb — yontem ve sembol proc'a donusur
def kare(n)
  n * n
end

puts method(:kare).to_proc.lambda?
puts :upcase.to_proc.lambda?
puts :upcase.to_proc.call("a")
```
```
true
true
A
```

Yöntemden ve sembolden elde edilen proc'ların `lambda?` değeri `true`. `:upcase.to_proc.call("a")` büyük harfli dizgiyi verdi. Bu, önceki derste görülen `select(&:even?)` biçiminin arkasındaki düzenektir: `&` sembolü bir proc'a çevirir ve bloğun yerine verir.

#### Değişkeni Taşımak

Bir proc, tanımlandığı kapsamdaki değişkenlere bağlı kalır. Bunu görmek için bir yöntem bir lambda döndürür ve lambda, yöntem bittikten sonra bile değişkeni artırmaya devam eder.

```ruby
# sayac.rb — lambda tanimlandigi yerdeki degiskeni tasir
def sayac_uret
  adet = 0
  -> { adet += 1 }
end

birinci = sayac_uret
ikinci = sayac_uret
puts birinci.call
puts birinci.call
puts ikinci.call
puts birinci.call
```
```
1
2
1
3
```

Her `sayac_uret` çağrısı kendi `adet` değişkenini kurdu. `birinci` sırayla `1`, `2` ve `3` verdi; `ikinci` kendi sayacından `1` verdi. Yöntem bitmişti, ama lambda değişkeni taşımaya devam etti. **KB13.** Bu ölçüm iki bağımsız sayacı gösterir; aynı değişkeni paylaşan iki lambda bu derste ölçülmez.

#### Nesneyi Blok Yerine Vermek

Bir proc ya da lambda, başına `&` konarak bir yönteme blok olarak verilebilir. Bu, Bloklar dersindeki `map` ile bu dersin nesnelerini birleştirir. `arity` ise nesnenin kaç parametre beklediğini sayı olarak verir.

```ruby
# blok_yerine.rb — nesneyi blok olarak vermek ve arity
iki_kat = ->(n) { n * 2 }
gevsek = proc { |x, y| [x, y] }
puts [1, 2, 3].map(&iki_kat).inspect
puts "arity lambda=#{iki_kat.arity} proc=#{gevsek.arity}"
puts [[1, 2], [3, 4]].map(&gevsek).inspect
```
```
[2, 4, 6]
arity lambda=1 proc=2
[[1, 2], [3, 4]]
```

`map(&iki_kat)` her elemanı lambda'ya verip `[2, 4, 6]` topladı: ayrı bir blok yazmak gerekmedi. `arity` lambda için `1`, iki parametreli proc için `2` döndü. İç içe dizilerde proc her alt diziyi iki parametreye bölüp `[x, y]` dizisini yeniden kurdu, bu yüzden sonuç girdiyle aynı göründü. **KB14.** Bu ölçümde lambda her zaman tek elemanlı çağrıldı; lambda'ya `&` ile iç içe dizi verildiğinde ne olduğu ölçülmedi.

#### return Nerede Döner

Proc ile lambda arasındaki bir fark da `return`'ün nereye döndüğüdür. Aşağıdaki iki yöntem aynı işi yapar: içeride bir nesne kurar, çağırır ve ardından `2` döndürmeye çalışır.

```ruby
# donus_farki.rb — proc ve lambda icinde return
def proc_ile
  p = proc { return 1 }
  p.call
  2
end

def lambda_ile
  l = lambda { return 1 }
  l.call
  2
end

puts proc_ile
puts lambda_ile
```
```
1
2
```

`proc_ile` `1` döndürdü: proc içindeki `return`, proc'u değil onu çevreleyen yöntemi bitirdi ve `2` satırına hiç ulaşılmadı. `lambda_ile` ise `2` döndürdü: lambda içindeki `return` yalnız lambda'yı bitirdi, yöntem kaldığı yerden sürdü. **KB30.** Ölçüm yalnız yöntem içinde yapıldı; üst düzeyde bir proc içindeki `return` bu derste denenmedi.

#### Kısmi Uygulama

`curry`, çok parametreli bir lambda'yı her çağrıda bir parametre alan bir zincire çevirir. Parametrelerin bir kısmı verilince geriye kalanı bekleyen yeni bir nesne döner.

```ruby
# curry.rb — curry ile parametre parametre cagirma
topla = ->(a, b, c) { a + b + c }
puts topla.curry[1][2][3]
puts topla.curry.(1, 2).(3)
kismi = topla.curry[1]
puts kismi.class
puts kismi.lambda?
```
```
6
6
Proc
true
```

Üç köşeli parantezle ve `(1, 2).(3)` biçimiyle `curry` aynı sonucu, `6`'yı verdi. `topla.curry[1]` bir `Proc` döndürdü ve bu nesne hâlâ bir lambda'ydı. **KB31.** `curry` yalnız sabit parametreli bir lambda ile ölçüldü.

#### Yöntem Nesnesi

`method(:ad)` bir yöntemi nesneye çevirir. Bu nesne çağrılabilir, sahibi sorulabilir ve `&` ile bloğa dönüştürülebilir.

```ruby
# yontem_nesnesi.rb — method ile Method nesnesi ve to_proc
def kare(n) = n * n
m = method(:kare)
puts m.class
puts m.call(4)
puts "sahip=#{m.owner} arity=#{m.arity}"
puts [1, 2, 3].map(&m).inspect
puts m.to_proc.class
```
```
Method
16
sahip=Object arity=1
[1, 4, 9]
Proc
```

`method(:kare)` bir `Method` nesnesi verdi ve `call(4)` `16` döndürdü. Üst düzeyde tanımlanan `kare`'nin sahibi `Object` olarak basıldı. `map(&m)` yöntemi blok yerine verdi ve `[1, 4, 9]` üretti; `to_proc` ise bir `Proc` döndürdü. **KB32.** Ölçüm yalnız üst düzeyde tanımlı bir yöntemle yapıldı.

#### Blok ile Proc Arasındaki Dönüşüm

Bir yöntemin son parametresi `&blok` ise, kendisine verilen blok bir `Proc` nesnesine çevrilir. Tersine, bir lambda `&` ile bloğun yerine verilebilir. Aşağıdaki ölçüm, bu iki yönde nesnenin kimliğinin korunup korunmadığına bakar.

```ruby
# blok_proc.rb — blok ve lambda arasindaki donusum
def al(&blok) = blok
b = al { |x| x + 1 }
puts b.class
puts b.lambda?
puts b.call(1)
l = ->(x) { x + 1 }
puts al(&l).equal?(l)
puts al(&l).lambda?
```
```
Proc
false
2
true
true
```

Bloktan dönen nesne bir `Proc` oldu ve `lambda?` `false` verdi. Bir lambda `&l` ile verilince ise `al` aynı nesneyi geri döndürdü: `equal?` `true` ve `lambda?` hâlâ `true`. Yani `&` bir lambda'yı proc'a dönüştürmedi. **KB33.** Ölçüm yalnız tek parametreli bir blok ve bir lambda ile yapıldı.

#### Çağrılabilir Nesneleri Birleştirmek

`>>` ve `<<` iki çağrılabilir nesneyi tek bir `Proc` içinde birleştirir. `f >> g` önce `f`'yi sonra `g`'yi uygular; `f << g` ise tersini yapar. `Method` nesnesi de aynı birleştirmeye katılabilir.

```ruby
# birlestir.rb — proc birlestirme
art = ->(x) { x + 1 }
iki = ->(x) { x * 2 }
puts (art >> iki).call(3)
puts (art << iki).call(3)
puts (art >> iki).class
puts (method(:Integer) >> art).call("4")
```
```
8
7
Proc
5
```

`(art >> iki).call(3)` önce `art`'ı sonra `iki`'yi uyguladı: `(3 + 1) * 2 = 8`. `(art << iki).call(3)` sırayı ters çevirdi: `art(iki(3)) = 7`. Birleştirme sonucu bir `Proc` oldu. `method(:Integer) >> art` bir `Method` nesnesini lambda ile birleştirdi: `"4"` önce `4` oldu, sonra `5` verdi. **KB45.** Ölçüm tek parametreli lambda'larla yapıldı.

#### arity ve parameters

`arity` bir çağrılabilir nesnenin kaç parametre beklediğini, `parameters` ise her birinin türünü bildirir. Proc ile lambda aynı parametre listesini farklı bildirebilir.

```ruby
# arity.rb — arity ve parameters
puts proc { |a, b| }.arity
puts lambda { |a, b| }.arity
puts proc { |*a| }.arity
puts lambda { |a, b = 1| }.arity
puts lambda { |a, b = 1| }.parameters.inspect
puts proc { |a, b| }.parameters.inspect
```
```
2
2
-1
-2
[[:req, :a], [:opt, :b]]
[[:opt, :a], [:opt, :b]]
```

`proc { |a, b| }` ve `lambda { |a, b| }` ikisi de `2` bildirdi. `*a` ile `-1`, bir zorunlu ve bir isteğe bağlı parametreyle `-2` basıldı: iki ölçümde de negatif değer, zorunlu parametre sayısının bir fazlasının eksilisiydi. `parameters` farkı gösterir: lambda'da `a` zorunlu (`:req`), proc'ta aynı parametre isteğe bağlı (`:opt`). Proc ile Lambda Arasındaki Fark bölümündeki gevşeklik böylece nesnenin kendi tanımında okunur. **KB46.**

#### Kapanış Değişkeni Paylaşır

Lambda, dıştaki yerel değişkenin değerini değil değişkenin kendisini taşır. Değişken sonradan değişirse lambda yeni değeri görür; lambda değişkene yazarsa dıştaki değişken de değişir.

```ruby
# paylasim.rb — kapanis ve dis degisken
x = 1
oku = -> { x }
x = 5
puts oku.call
yaz = -> { x = 9 }
yaz.call
puts x
```
```
5
9
```

`oku` tanımlandığında `x` `1`'di; `x` sonradan `5` yapıldı ve `oku.call` `5` verdi. `yaz` içindeki `x = 9` dıştaki `x`'e yazdı ve `puts x` `9` bastı. Lambda bir kopya değil, tanımlandığı yerdeki değişkeni kullanır. **KB47.** Ölçüm üst düzeydeki yereller içindir; yöntem sınırını geçen durum Değişkeni Taşımak bölümünde ölçüldü.

#### case ile Lambda

`case` ifadesi `when` koşulunu `===` ile sınar. Bir lambda, `Proc#===` sayesinde bu sınamaya doğrudan katılabilir: `===` lambda'yı verilen değerle çağırır.

```ruby
# case_lambda.rb — when kolunda lambda
cift = ->(n) { n.even? }
[1, 2].each do |n|
  case n
  when cift then puts "#{n} cift"
  else puts "#{n} tek"
  end
end
puts cift === 2
puts "sahip=#{cift.method(:===).owner}"
```
```
1 tek
2 cift
true
sahip=Proc
```

`when cift` lambda'yı her `n` için çağırdı: `1` için `false` döndüğünden `else` koştu, `2` için `true` döndüğünden `when` kolu koştu. `cift === 2` doğrudan `true` verdi ve `===`'ün sahibi `Proc` olarak basıldı. Böylece `case`, koşulu sınarken lambda'ya `===` iletisini gönderir ve yanıtlayan bir sınıftır. Bu, Proc ve Lambda dersinin ortak sorusuna bağlanır: aynı çağrılabilir nesne `call` ile, `.()` ile, `&` ile blok yerine ve burada `===` ile kullanılabilir; her birinde nesnenin kendisi aynıdır, yalnız ileti değişir. **KB54.** Ölçüm tek parametreli bir lambda ile yapıldı; çok parametreli lambda'nın `when` kolunda davranışı denenmedi. `===` kolunun değeri `case`'in kendi sınamasında kullanıldığı için bu ölçümde ayrıca bir sonuç değişkenine atanmadı.

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

Proc ile lambda arasındaki farkı `call`'ın sahibine bakarak görebilir miyiz? Hayır: iki tür için de sahip aynıdır, çünkü ikisi de `Proc` sınıfının örneğidir. Fark sahipte değil, nesnenin durumundadır.

```ruby
# sahip.rb — call iletisinin sahibi iki turde de ayni
gevsek = proc { |x| x }
kati = lambda { |x| x }
puts "proc    #{gevsek.method(:call).owner} lambda?=#{gevsek.lambda?}"
puts "lambda  #{kati.method(:call).owner} lambda?=#{kati.lambda?}"
```
```
proc    Proc lambda?=false
lambda  Proc lambda?=true
```

Sahip iki satırda da `Proc`; yalnız `lambda?` farklı. Önceki derslerde `owner` sorusu bir yöntemin hangi sınıfta ya da modülde tanımlı olduğunu ayırıyordu; burada iki nesne aynı sınıftan olduğu için aynı soru bir farkı ortaya çıkarmıyor. Aynı iletiyi iki nesneye göndermek iki ayrı yanıtlayanı değil, tek yanıtlayanın iki durumunu veriyor. Yani `owner` sorusu, `proc` ile lambda arasındaki farkı göstermez: sınıf aynıdır, farkı nesnenin kendi durumu taşır. **KB12.** Bu ölçüm yalnız `call` iletisini sorar; başka iletilerde sahip farkı olup olmadığı bu derste ölçülmez.

#### Özet

- Proc ve lambda, bir bloğu nesneye çeviren iki yoldur; ikisinin de sınıfı `Proc`'tur.
- Bu dersin yanıtlayanı **sınıf**tır: `call` iletisini `Proc` yanıtlar ve `call`, `.()` ve köşeli parantez aynı sonucu verir.
- Lambda argüman sayısını denetler ve fazlalıkta `ArgumentError` fırlatır; proc fazlayı bırakır, eksiği `nil` yapar.
- Yöntem nesnesinden ve sembolden elde edilen proc'lar `lambda?` bakımından `true` döner.
- Sınırlayıcı ölçüm: `call` sahibi iki tür için de `Proc`'tur; fark `lambda?` durumundadır.

#### Sonraki Adım

Bir lambda'nın hatası argüman sayısıydı. Sonraki ders hata kavramının kendisine geçer: `rescue` bir yöntem değil, dilin kendi yapısıdır ve bu yapının hangi ifadelerde ne döndürdüğü ölçülecektir.

### İstisnalar

Source: https://academia.sh/tr/kurslar/ruby-temelleri/istisnalar

rescue, ensure ve retry yapılarının dilin kendi sözdizimi olduğu, raise'in ise bir ileti olduğu ve rescue dalındaki eşleşmenin bir sınıf iletisiyle yapıldığı ölçülür.

Önceki dersin sonunda bir lambda yanlış sayıda argümanla çağrıldığında `ArgumentError` fırlattı ve bu istisna `rescue` ile yakalandı. Bu ders o yapıyı konu yapar. **İstisna (exception)**, normal akışı kesen bir olaydır; **fırlatma (raise)** onu üretir, **yakalama (rescue)** onu karşılar. M12/K01 PHP'de hata kanallarını ölçmüştü; burada hata kanalı sorusu sorulmuyor, yalnız yapının kim tarafından yanıtlandığı ölçülüyor.

Bu dersin yanıtlayanı **sözdizimi**dir: `rescue` bir yöntem değil, dilin kendi yapısıdır.

#### rescue Bir İfadedir

Çekirdek gözlemi `defined?` sorusuna dayanır: bir `begin ... rescue ... end` bloğu için `defined?` ne diyor? Yöntem çağrısı olsaydı `"method"` dönerdi.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

```ruby
# kurtarma.rb — rescue yapisini kim yanitliyor
require_relative "yanit"

tanim = defined?(begin; Integer("x"); rescue ArgumentError; 0; end)
tur, kanit = yanitlayan(tanim)
puts "#{tur}  #{kanit}"
deger = Integer("x") rescue 0
puts deger
puts Integer("x", exception: false).inspect
```
```
sozdizimi  defined? expression
0
nil
```

İlk satır çekirdeğin `kurtarma` gözlemidir: yanıtlayan **sözdizimi**, `defined?` `"expression"` verdi. İkinci satır deyim değiştirici biçimi gösterir: `Integer("x") rescue 0` istisnayı yakalayıp `0` verdi. Üçüncü satır aynı işi `exception: false` anahtarlı argümanıyla, istisna fırlatmadan yaptı ve `nil` döndürdü. **KB15.** Bu ders değiştirici `rescue`'ü yalnız `ArgumentError` ile ölçer; başka istisna sınıflarını yakalayıp yakalamadığı ölçülmez.

İkinci satırda `Integer("x") rescue 0` önce bir değişkene atandı, sonra basıldı; değiştirici biçim bir yöntem argümanının ya da `defined?`'ın içine doğrudan yazılmadı. Bu konumlarda nasıl çözüldüğü bu derste ölçülmez.

#### İstisna Sınıfları

Her istisna bir sınıfın örneğidir. `e.class` onun türünü verir; iletisi (`e.message`) yerine sınıf adı kullanılır, çünkü ileti metni sürümle değişebilir. Dersin kendi `raise` metni bu kuralın dışındadır.

```ruby
# siniflar.rb — istisna siniflari
def dene
  yield
rescue => e
  e.class
end

puts dene { 1 / 0 }
puts dene { raise "x" }
puts dene { Integer("x") }
puts 1.0 / 0

class StokBitti < StandardError; end

begin
  raise StokBitti, "stok bitti"
rescue StokBitti => e
  puts "#{e.class} #{e.message}"
end
```
```
ZeroDivisionError
RuntimeError
ArgumentError
Infinity
StokBitti stok bitti
```

Sıfıra tam sayı bölmesi `ZeroDivisionError` fırlattı; ondalık bölme ise istisna değil `Infinity` verdi. Düz `raise "x"` bir `RuntimeError` üretti. Kendi sınıfımız `StokBitti`, `StandardError`'dan türedi ve `rescue StokBitti => e` yalnız onu yakaladı. İleti burada dersin kendi `raise` metni olduğu için basılabildi.

#### ensure ve retry

`ensure` dalı istisna olsa da olmasa da koşar; kaynak temizleme için kullanılır. `retry` ise `begin` bloğunu baştan çalıştırır.

```ruby
# deneme.rb — ensure ve retry
deneme = 0
begin
  deneme += 1
  raise "henuz degil" if deneme < 3
  puts "basardi #{deneme}"
rescue
  retry
end

olaylar = []
begin
  olaylar << 1
  raise "hata"
rescue
  olaylar << :kurtarildi
ensure
  olaylar << 2
end
puts olaylar.inspect
```
```
basardi 3
[1, :kurtarildi, 2]
```

`retry` her başarısız denemede bloğu yeniden başlattı ve üçüncü denemede iş tamamlandı. İkinci blokta `ensure` dalı, istisna yakalandıktan sonra da koştu. **KB16.** Yeniden deneme sayısı dersin kendi sayacıdır; gerçek bir yeniden deneme senaryosunda sonsuz döngüyü önlemek için bir üst sınır gerekir ve bu burada ölçülmez.

#### Dallar ve else

Bir `begin` bloğu birden çok `rescue` dalı taşıyabilir; istisna ilk eşleşen dala gider. `else` dalı yalnız hiçbir istisna olmadığında koşar. Bir sınıfın üst sınıfı `superclass` ile sorulur.

```ruby
# dallar.rb — rescue dallari ve else
def sinifla(deger)
  Integer(deger)
rescue ArgumentError
  "gecersiz"
rescue TypeError
  "tip"
else
  "tamam"
end

puts sinifla("5")
puts sinifla("x")
puts sinifla(nil)
puts ZeroDivisionError.superclass
puts StandardError.superclass
```
```
tamam
gecersiz
tip
StandardError
Exception
```

`"5"` istisna doğurmadığı için `else` dalı koştu. `"x"` `ArgumentError` dalına, `nil` ise `TypeError` dalına gitti: ilk eşleşen dal kazandı. `ZeroDivisionError`'ın üst sınıfı `StandardError`, onun üst sınıfı ise `Exception` çıktı; çıplak `rescue` bu yüzden `ZeroDivisionError`'ı yakalar. **KB18.** Dalların sırası önemlidir: geniş bir sınıf önce yazılırsa ondan sonraki dallar hiç koşmaz, ama bu davranış burada ölçülmez.

#### StandardError ve Exception

Parametresiz `rescue` ve `rescue => e` yalnız `StandardError` ve alt sınıflarını yakalar. Hiyerarşide `StandardError`'ın dışında kalan istisnalar bu yakalayıcıdan geçer. Aşağıdaki ölçüm bir `ArgumentError`'ın yakalandığını, bir `NotImplementedError`'ın yakalanmadığını ve ilgili üst sınıfları gösterir.

```ruby
# hiyerarsi.rb — rescue hangi istisnalari yakalar
def sar
  yield
  "yok"
rescue => e
  "yakalandi #{e.class}"
end

puts sar { raise ArgumentError }
begin
  sar { raise NotImplementedError }
rescue NotImplementedError => e
  puts "disarida #{e.class}"
end
puts NotImplementedError.superclass
puts ScriptError.superclass
puts KeyError.superclass
puts IndexError.superclass
```
```
yakalandi ArgumentError
disarida NotImplementedError
ScriptError
Exception
IndexError
StandardError
```

`ArgumentError` yakalandı ve `yakalandi ArgumentError` basıldı. `NotImplementedError` ise `sar`'ın `rescue`'undan geçti ve dıştaki `begin` bloğunda yakalandı. Üst sınıf zinciri bunu açıklar: `NotImplementedError` `ScriptError`'ın, o da `Exception`'ın altındadır; `StandardError`'a ulaşmaz. `KeyError` ise `IndexError` üzerinden `StandardError`'a bağlıdır. **KB34.** Yalnız bu dört sınıfın üst zinciri basıldı.

#### ensure Dönüş Değerini Değiştirmez

`ensure` dalı, yöntem nasıl biterse bitsin koşar. Ancak bu dalın son ifadesi yöntemin dönüş değeri olmaz. `return` ile erken çıkıldığında da `ensure` koşar.

```ruby
# ensure_deger.rb — ensure dali ve donus degeri
def deger
  1
ensure
  2
end

def erken
  return 1
ensure
  puts "temizlik"
end

puts deger
puts erken
```
```
1
temizlik
1
```

`deger` `1` döndürdü; `ensure` içindeki `2` dönüş değerini değiştirmedi. `erken` `return 1` ile çıktı, ama `ensure` önce `temizlik` satırını bastı ve ardından `1` döndü. **KB35.** Yalnız bu iki biçim ölçüldü; `ensure` içinde `return` yazılması ölçülmedi.

#### Yeniden Fırlatma

`rescue` içinde argümansız `raise`, yakalanan istisnayı olduğu gibi yeniden fırlatır. Böylece bir katman olayı kaydeder, ama kararı üst katmana bırakır.

```ruby
# yeniden.rb — raise ile yeniden firlatma
def disari
  yield
rescue ArgumentError => e
  puts "kayit #{e.class}"
  raise
end

begin
  disari { Integer("x") }
rescue => e
  puts "yeniden #{e.class}"
end
```
```
kayit ArgumentError
yeniden ArgumentError
```

İç `rescue` önce `kayit ArgumentError` satırını bastı ve argümansız `raise` ile aynı istisnayı fırlattı. Dıştaki `rescue` aynı sınıfı gördü: `yeniden ArgumentError`. **KB36.** İstisna iletisi basılmadı; yalnız sınıf ölçüldü.

#### retry Sınırı ve Neden Zinciri

`retry` bloğu baştan koşturur; sınırsız bırakılırsa sonsuz döngüye girer. Bu yüzden her zaman bir sayaçla birlikte yazılır. Bir `rescue` içinde yeni bir istisna fırlatılırsa, önceki istisna `cause` ile yeni olanın üzerinde kalır.

```ruby
# sinir.rb — retry sayaci ve cause
deneme = 0
begin
  deneme += 1
  raise "x" if deneme < 10
rescue
  retry if deneme < 3
  puts "vazgecti #{deneme}"
end

begin
  begin
    Integer("x")
  rescue => e
    raise RuntimeError, "ust"
  end
rescue => ust
  puts ust.cause.class
end
```
```
vazgecti 3
ArgumentError
```

İlk iki turda `retry` bloğu yeniden koşturdu; üçüncü turda koşul sağlanmadığı için `vazgecti 3` basıldı. İkinci bölümde `Integer("x")` bir `ArgumentError` fırlattı, `rescue` içinde yeni bir `RuntimeError` fırlatıldı ve dıştaki `ust.cause.class` `ArgumentError` verdi. **KB37.** `retry` yalnız `begin` ve `rescue` biçiminde ölçüldü.

#### Çoklu Sınıf ve Dal Sırası

Bir `rescue` birden çok sınıfı virgülle sayabilir. Birden çok `rescue` dalı varsa, ilk eşleşen dal koşar ve sonrakilere bakılmaz.

```ruby
# siralama.rb — rescue listesi ve dal sirasi
def sinifla
  yield
rescue ZeroDivisionError, TypeError
  "ikisi"
rescue StandardError
  "genel"
end
puts sinifla { 1 / 0 }
puts sinifla { 1 + nil }
puts sinifla { Integer("x") }
def ters
  yield
rescue StandardError
  "genel"
rescue ZeroDivisionError
  "ozel"
end
puts ters { 1 / 0 }
```
```
ikisi
ikisi
genel
genel
```

`rescue ZeroDivisionError, TypeError` hem `1 / 0`'ı hem `1 + nil`'i yakaladı: ikisi de `ikisi` basıldı. `Integer("x")` bu listede yoktu ve `genel` dalına düştü. `ters` yönteminde `StandardError` önce yazıldı; `1 / 0` bu dala takıldı ve `ozel` dalına hiç ulaşılmadı. Özel sınıf, genel olandan önce yazılmalıdır. **KB48.** Ölçüm bu üç istisna sınıfıyla yapıldı.

#### begin, else ve ensure Sırası

Bir `begin` bloğundaki dallar belirli bir sırayla koşar. Dalların hangi sırayla çalıştığı, bir diziye eklenen sembollerle okunur.

```ruby
# sira.rb — dallarin kosma sirasi
sira = []
begin
  sira << :govde
rescue
  sira << :kurtarma
else
  sira << :else
ensure
  sira << :ensure
end
puts sira.inspect
sira = []
begin
  sira << :govde
  raise "x"
rescue
  sira << :kurtarma
else
  sira << :else
ensure
  sira << :ensure
end
puts sira.inspect
```
```
[:govde, :else, :ensure]
[:govde, :kurtarma, :ensure]
```

Hata yokken `else` gövdeden sonra, `ensure` en son koştu. Hata fırlatılınca `rescue` devreye girdi, `else` atlandı ve `ensure` yine koştu. `ensure` iki durumda da son sırada yer aldı. **KB49.**

#### Alanlı İstisna Sınıfı

Kendi istisna sınıfınız ek bilgi taşıyabilir. `initialize` içinde alanı saklar, `super` ile iletiyi verirsiniz.

```ruby
# alanli.rb — kendi alani olan istisna sinifi
class StokHatasi < StandardError
  attr_reader :adet

  def initialize(adet)
    @adet = adet
    super("stok yetersiz")
  end
end

begin
  raise StokHatasi.new(3)
rescue StokHatasi => e
  puts "#{e.class} adet=#{e.adet} ileti=#{e.message}"
end
puts StokHatasi.superclass
puts StokHatasi.instance_method(:adet).owner
```
```
StokHatasi adet=3 ileti=stok yetersiz
StandardError
StokHatasi
```

`StokHatasi` `StandardError`'dan türedi, `adet` alanını taşıdı ve `super` ile kendi ileti metnini verdi. `rescue` alana `e.adet` ile eriştiği için `adet=3` basıldı; `ileti=stok yetersiz` dersin kendi yazdığı metindir. `adet` okuyucusunun sahibi `StokHatasi`'dır: bu yöntemi yanıtlayan sınıftır. **KB50.**

#### catch ve throw

`catch` ve `throw` etiketli bir çıkış sağlar. `throw`, aynı etiketle açılmış en yakın `catch` bloğuna kadar çıkar ve ona bir değer taşır.

```ruby
# etiket.rb — catch ve throw
r = catch(:bitti) do
  [1, 2, 3].each { |n| throw :bitti, n * 10 if n == 2 }
  :hic
end
puts r
puts "sahip=#{method(:throw).owner}"
```
```
20
sahip=Kernel
```

`throw :bitti, 20` iç `each` bloğunu ve dıştaki `catch` bloğunu bitirdi; `catch` `:hic` yerine `20` döndürdü. `throw`'un sahibi `Kernel` olarak basıldı. **KB51.** Yalnız bu tek etiketli örnek ölçüldü.

#### İç İçe ensure ve rescue Eki

İç bir `begin` bloğunda `ensure` varsa ve istisna orada yakalanmıyorsa, istisna dışarı çıkmadan önce `ensure` koşar. Kısa bir yazım olarak `ifade rescue değer`, ifade hata verirse değeri kullanır; bu yazım parantezle bir değişkene atanabilir.

```ruby
# ic_ice.rb — ic ice ensure ve rescue eki
kayit = []
begin
  begin
    kayit << :ic
    raise ArgumentError
  ensure
    kayit << :temizlik
  end
rescue ArgumentError
  kayit << :dis
end
puts kayit.inspect
kurtarilan = (Integer("x") rescue -1)
puts kurtarilan
```
```
[:ic, :temizlik, :dis]
-1
```

İç blokta istisna yakalanmadı; ama dışa çıkmadan önce `ensure` koştu ve sıra `[:ic, :temizlik, :dis]` oldu. Yani `ensure`, dıştaki `rescue`'dan önce çalışır. `Integer("x")` hata verince ek yazımdaki `-1` kullanıldı. **KB55.** Ek yazımın yalnız `StandardError` ailesini yakaladığı ölçülmedi; bu derste yalnız bu tek örnek denendi.

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

`raise` bir ileti, yani bir yöntem çağrısıdır; `rescue` ise sözdizimidir. İkisinin yanıtlayanı farklıdır. Bir de `rescue ArgumentError => e` dalının kendi içinde bir eşleşme yaptığı sorulabilir: dal, istisnayı `ArgumentError` sınıfına bir iletiyle mi soruyor?

```ruby
# eslesme.rb — rescue dalinin eslesmesi
puts "raise sahibi=#{method(:raise).owner}"

begin
  Integer("x")
rescue ArgumentError => e
  puts ArgumentError === e
  puts "=== sahibi=#{ArgumentError.method(:===).owner}"
end
```
```
raise sahibi=Kernel
true
=== sahibi=Module
```

`raise` bir yöntemdir ve sahibi `Kernel` modülüdür. Dalın eşleşmesi `===` iletisiyle yapılır: `ArgumentError === e` `true` verdi ve `===`'in sahibi `Class` değil `Module`'dür, yani sınıf kendi `===`'ini üst yapıdan alır. Bu, ayrımı netleştirir: `rescue` anahtar sözcüğünü dil çözer, ama hangi dalın istisnayı alacağını sınıfın `===` yanıtı belirler. Dil yapıyı kurar, sınıf eşleşmeyi karar verir. Aynı `rescue` satırında yazılan sınıf bu yüzden sıradan bir sınıf nesnesidir ve kendi eşleşme iletisini yanıtlar. **KB17.** Bu ölçüm yalnız `ArgumentError` dalı için yapıldı; farklı istisna sınıflarının aynı iletiyi aynı sahipten aldığı varsayılmaz.

Sonuç, çekirdeğin sınıflandırmasıyla uyumludur: `defined?` ifadeyi `"expression"` olarak adlandırdı, yani `rescue` anahtar sözcüğü bir ileti değildir; dal eşleşmesi ise yukarıda görüldüğü gibi `===` iletisiyle yapılır. Fırlatan taraf ise farklıdır; `raise` sıradan bir yöntemdir, `Kernel` modülünden gelir ve istisna sınıfını ya da metni argüman olarak alır. Böylece bir istisna olayında iki yanıtlayan yan yana durur: fırlatmayı bir modül, yakalamayı dilin kendisi yanıtlar. Dersin yanıtlayanı olarak **sözdizimi**nin seçilmesinin nedeni de budur: ölçülen soru, yakalama yapısının kim tarafından kurulduğudur.

#### Özet

- İstisna, normal akışı kesen bir olaydır; `rescue` yakalar, `raise` fırlatır, `ensure` her durumda koşar, `retry` bloğu yeniden başlatır.
- Bu dersin yanıtlayanı **sözdizimi**dir: `defined?` `begin ... rescue ... end` için `"expression"` döndürür.
- Deyim değiştirici `rescue` ve `exception: false` aynı işi iki yolla görür: istisna yerine bir değer.
- `raise` bir yöntemdir ve sahibi `Kernel` modülüdür; `rescue` dalının eşleşmesi `===` iletisiyle yapılır.
- Sınırlayıcı ölçüm: `rescue` sözdizimidir ama dalının içindeki eşleşme bir sınıf iletisidir.

#### Sonraki Adım

Bu derste istisna fırlatan işlemlerin hepsi bellekte çalıştı. Son derste dosyalar ve girdi çıktı gelir: bir dosyayı açan blok mu, yöntem mi, ve `ensure` bu işte hangi yapıyı temizler.

### Dosya ve Girdi/Çıktı

Source: https://academia.sh/tr/kurslar/ruby-temelleri/dosya-ve-girdi-cikti

File.open çağrısında sonucu bloğun, dosyanın kapanmasını ise yöntemin belirlediği ölçülür; blok istisna fırlatsa da dosya kapanır. Ders, kursun on iki gözleminin kapanış tablosunu da verir.

Önceki dersin sonunda `ensure` her durumda koşan bir dal olarak görüldü. Dosyalar bu yapının en sık kullanıldığı yerdir: açılan bir dosya kapatılmak zorundadır ve kapatmayı unutmak bir sızıntıdır. Ruby'de `File.open` çağrısına bir blok verilebilir; soru, kapatma işinin kime ait olduğudur. **Girdi/çıktı (input/output)**, programın dış dünyayla, yani dosyalarla ve standart akışlarla veri alışverişidir.

Bu dersin yanıtlayanı **blok**tur: `File.open` iletisi aynı dosyaya iki farklı blokla iki farklı sonuç verir. Dersin sonunda kursun on iki gözleminin tamamı tek tabloda toplanır.

#### Dosya Okuma Gözlemi

Çekirdeğin on ikinci gözlemi `dosya okuma`'dır. Sınıflandırma yardımcısı `yanit.rb` kursun başından beri aynıdır ve burada yalnız diske yazılır.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

```ruby
# dosya_okuma.rb — ayni File.open iki blokla iki sonuc verir
require_relative "yanit"

File.write("not.txt", "ilk satir\nikinci satir\n")

tanim = defined?(File.open("not.txt"))
tur, kanit = yanitlayan(tanim, bloklu: -> {
  [File.open("not.txt") { |f| f.gets.chomp }, File.open("not.txt") { |f| f.readlines.size }]
})
puts tanim
puts "#{tur}  #{kanit}"
puts File.method(:open).owner.instance_of?(Class)
```

```
method
blok  "ilk satir" / 2
true
```

`File.write` iki satırlık `not.txt` dosyasını yazdı. `defined?` bloksuz çağrıyı `"method"` olarak bildirdi, yani ortada bir ileti var. İlk blok ilk satırı, ikinci blok satır sayısını döndürdü: aynı ileti, `"ilk satir"` ve `2`. Yanıtlayan **blok**tur. `File.open`'ın sahibi bir sınıftır (`instance_of?(Class)` `true`); sahibin tekil sınıf olarak görünmesi ikinci kursun konusudur ve burada yalnız sonucu okunur. **KB56.** Ölçüm tek bir dosya ve iki blokla yapıldı.

#### Satır Okuma ve Yazma Akışları

Bloksuz kısa yollar, dosyayı açıp kapatma işini kendileri yapar. `File.write` yazılan bayt sayısını döndürür; `File.readlines` satırları bir dizi yapar ve `chomp: true` satır sonunu atar; `File.foreach` satırları tek tek verir. Olmayan bir dosya `Errno::ENOENT` fırlatır. Hata iletisi yol taşıdığı için yalnız sınıf basılır.

```ruby
# akislar.rb — yazma, okuma ve eksik dosya
File.write("not.txt", "ilk satir\nikinci satir\n")

puts File.write("b.txt", "abc")
puts File.readlines("not.txt", chomp: true).inspect
puts File.foreach("not.txt").count
begin
  File.read("yok.txt")
rescue Errno::ENOENT => e
  puts e.class
end
```

```
3
["ilk satir", "ikinci satir"]
2
Errno::ENOENT
```

`abc` üç bayttır ve `File.write` `3` döndürdü. `readlines` iki dizgiden oluşan bir dizi verdi. `foreach` bloksuz çağrıldığında bir yineleyici döndürür; `count` onu tüketip `2` buldu. Eksik dosya `Errno::ENOENT` fırlattı ve dal onu yakaladı. **KB57.** `File.write` yalnız dizgi içerikle ölçüldü.

Açık bir dosyayla çalışırken satır okuma yöntemleri örneğin üzerinde çağrılır. Bunların sahibi de ölçülebilir.

```ruby
# sahipler.rb — dosya ve standart cikti yontemlerinin sahibi
File.write("not.txt", "ilk satir\nikinci satir\n")

File.open("not.txt") do |f|
  puts f.class
  puts f.method(:gets).owner
  puts f.method(:each_line).owner
end
puts $stdout.method(:puts).owner
```

```
File
IO
IO
IO
```

Dosya nesnesi bir `File`'dır, ama `gets` ve `each_line` onun kendi sınıfında değil `IO` sınıfında tanımlıdır. `$stdout.puts` de aynı `IO` sınıfındadır. Dosya ile standart çıktı aynı sınıf ailesinin üyesidir ve bu üçünde de yanıtlayan sınıftır. **KB58.** Sahipler Ruby 4.0.7'de ölçüldü; hangi yöntemin hangi sınıfta tanımlı olduğu sürümden sürüme taşınabilir.

#### Açık Kalan ve Kapanan Dosya

`File.open` bloksuz çağrılırsa dosya nesnesini döndürür ve dosya açık kalır; kapatmak çağıranın işidir. Bloklu çağrıda ise dosya nesnesi bloğa verilir, blok bitince dosya kapanır ve çağrının değeri bloğun son ifadesidir.

```ruby
# kapanma.rb — bloksuz ve bloklu File.open
File.write("not.txt", "ilk satir\nikinci satir\n")

f = File.open("not.txt")
puts f.class
puts f.closed?
f.close
puts f.closed?

tutulan = nil
deger = File.open("not.txt") do |dosya|
  tutulan = dosya
  puts dosya.closed?
  42
end
puts deger
puts tutulan.closed?
```

```
File
false
true
false
42
true
```

Bloksuz çağrı `File` döndürdü, `closed?` `false` çıktı ve ancak elle `close` çağrılınca `true` oldu. Bloklu çağrıda dosya blok içinde açıktı. Blok `42` ile bitti: dönüş değeri `42` oldu, blok dışına taşınan dosya nesnesi ise artık kapalıydı. Sonuç bloktan, kapanma yöntemden gelir. **KB59.** Ölçüm yerel dosya ve tek bir blok için yapıldı.

#### Açma Kipleri

`File.open` ikinci argüman olarak bir kip alır. `"w"` dosyayı açarken sıfırlar, `"a"` mevcut içeriğin sonuna ekler. Fark satır sayısıyla ölçülür. Bloklu `File.open` çağrısının değeri bloğun son ifadesidir; `write` yazılan bayt sayısını verir.

```ruby
# kipler.rb — w sifirlar, a sonuna ekler
File.write("kip.txt", "bir\niki\n")

File.open("kip.txt", "a") { |f| f.puts "uc" }
puts File.readlines("kip.txt").size

donus = File.open("kip.txt", "w") { |f| f.write("tek\n") }
puts donus
puts File.readlines("kip.txt").size

File.open("kip.txt", "a") { |f| puts f.method(:puts).owner }
```

```
3
4
1
IO
```

İki satırlık dosyaya `"a"` ile bir satır eklenince sayı `3` oldu. `"w"` ile açılış dosyayı sıfırladı: blok `write` değerini, yani `4` baytı döndürdü ve satır sayısı `1`'e indi. `puts` yöntemi `IO` sınıfında tanımlıdır; yanıtlayan sınıftır. **KB60.** Ölçüm yalnız `"a"` ve `"w"` kipleriyle yapıldı; okuma ve yazma kipleri birlikte denenmedi.

#### puts ve print Farkı

Dosyaya yazarken `puts` satır sonu ekler, `print` eklemez. Fark, yazılan içeriğin `inspect` çıktısında görünür.

```ruby
# yazim_farki.rb — puts satir sonu ekler, print eklemez
File.open("fark.txt", "w") do |f|
  f.puts "a"
  f.print "b"
  f.puts "c"
end
puts File.read("fark.txt").inspect
puts File.size("fark.txt")
File.open("fark.txt") { |f| puts f.method(:print).owner }
```

```
"a\nbc\n"
5
IO
```

Dosyada `a`, satır sonu, `b`, `c` ve yine satır sonu var; `b` ile `c` bitişik yazıldı. Toplam beş bayt. `print` de `IO` sınıfındadır; yanıtlayan sınıftır. **KB61.** İçerik ASCII dizgilerdi; çok baytlı karakterlerde bayt sayısı karakter sayısından ayrışır ve burada ölçülmedi.

#### Var Olma ve Boyut

`File.exist?` bir yolun var olup olmadığını, `File.size` bayt boyutunu verir. Açık bir dosya nesnesinin kendi `size` yöntemi de vardır ve sahibi `File`'dır.

```ruby
# boyut.rb — var olma ve boyut
File.write("boyut.txt", "abcd")
puts File.exist?("boyut.txt")
puts File.exist?("yok.txt")
puts File.size("boyut.txt")
File.open("boyut.txt") do |f|
  puts f.size
  puts f.method(:size).owner
end
```

```
true
false
4
4
File
```

Var olan dosya için `true`, olmayan için `false` döndü. Boyut `4` bayttı ve açık nesne de aynı değeri verdi. Bu kez sahip `IO` değil `File`'dır: dosyaya özgü bilgi alt sınıfta durur, akış işleri üst sınıfta. **KB62.** Boş dosya ve silinmiş dosya ölçülmedi.

#### Satır Satır ve Enumerable

`each_line` blok verilmezse bir yineleyici döndürür ve `chomp: true` satır sonlarını atar. Yineleyicinin `map` yöntemi `Enumerable` modülünden gelir. Okuma, dönüştürme, süzme ve yazma bir akış olarak bağlanabilir.

```ruby
# akis.rb — oku, map, select, yaz
File.write("girdi.txt", "elma\nkar\narmut\nay\n")

satirlar = []
File.open("girdi.txt") { |f| f.each_line(chomp: true) { |s| satirlar << s } }
puts satirlar.inspect

yineleyici = File.foreach("girdi.txt", chomp: true)
puts yineleyici.method(:map).owner

sonuc = yineleyici.map(&:upcase).select { |s| s.size > 3 }
yazilan = File.write("cikti.txt", sonuc.join("\n") + "\n")
puts yazilan
puts File.read("cikti.txt").inspect
```

```
["elma", "kar", "armut", "ay"]
Enumerable
11
"ELMA\nARMUT\n"
```

`each_line` dört satırı sondaki satır sonu olmadan verdi. `File.foreach` blok almadan çağrılınca bir yineleyici döndürdü ve `map` onun için `Enumerable` modülündedir; yanıtlayan modüldür. `upcase` sonrası `select` dört harften uzun olanları bıraktı: `ELMA` ve `ARMUT`. Yazılan `ELMA`, satır sonu, `ARMUT`, satır sonu on bir bayttır. **KB63.** Akış tek geçişli ve küçük bir girdiyle ölçüldü; büyük dosyada belleğe ne olduğu ölçülmedi.

#### Standart Girdiyi Borudan Okumak

Standart girdi, programın dışarıdan satır aldığı akıştır ve `$stdin` ile okunur. Ders blokları etkileşimli girdi beklemez; bu yüzden girdi bir **boru (pipe)** ile verilir: bir komutun çıktısı, sonraki komutun girdisi olur.

```ruby
# oku.rb — standart girdiyi satir satir okur
ilk = $stdin.gets.chomp
kalan = $stdin.each_line.map(&:chomp)
puts ilk
puts kalan.inspect
puts $stdin.method(:gets).owner
```

```bash
# girdi.sh — oku.rb'yi borudan besler
printf 'bir\niki\nuc\n' | ruby oku.rb
```

```
bir
["iki", "uc"]
IO
```

`gets` ilk satırı okudu, `each_line` kalanları tüketti. `$stdin.gets` de `IO` sınıfındadır; dosyayı da klavyeyi de aynı sınıf okur. **KB64.** Girdi üç satırlık sabit bir dizgiydi; boş girdi ve çok büyük girdi ölçülmedi.

Aynı boru satır saymak için de kullanılır. `each_line` bloksuz çağrılınca bir yineleyici döndürür; `count` ona `Enumerable` modülünden gelir.

```ruby
# say.rb — standart girdideki satirlari sayar
puts $stdin.each_line.count
puts $stdin.method(:each_line).owner
puts $stdin.each_line.method(:count).owner
```

```bash
# say.sh — say.rb'yi borudan besler
printf 'a\nb\nc\nd\n' | ruby say.rb
```

```
4
IO
Enumerable
```

Dört satırlık girdi `4` olarak sayıldı. `each_line` yöntemi `IO` sınıfındadır, yineleyicinin `count` yöntemi ise `Enumerable` modülündedir; bu iki yanıtlayan aynı ifadede art arda çalışır. **KB65.** Girdi sabit ve dört satırlıktı; son satırı satır sonsuz biten girdi ölçülmedi.

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

Bu dersin yanıtlayanı blok, ama bu cümle yarım kalırsa yanıltır: blok sonucu belirler, kaynağı temizleyen blok değildir. Kapatmayı bloklu `File.open` yöntemi üstlenir. Bunu ayırmak için bloğun içinde bir istisna fırlatılır. Dosya nesnesi bloğun dışında tutulur, istisna yakalanır ve ardından dosyanın durumu sorulur. Karşılaştırma için aynı istisna bloksuz açılmış bir dosyanın yanında fırlatılır.

```ruby
# istisna_kapanis.rb — blok istisna firlatsa da dosya kapanir
File.write("not.txt", "ilk satir\nikinci satir\n")

tutulan = nil
begin
  File.open("not.txt") do |dosya|
    tutulan = dosya
    raise "blok koptu"
  end
rescue => e
  puts e.class
end
puts tutulan.closed?

elle = File.open("not.txt")
begin
  raise "elle acik"
rescue => e
  puts e.class
end
puts elle.closed?
elle.close
puts elle.closed?
```

```
RuntimeError
true
RuntimeError
false
true
```

İlk durumda blok `RuntimeError` fırlattı, dal onu yakaladı ve dosya yine de kapalıydı (`true`). İkinci durumda aynı istisna bloksuz açılmış dosyanın yanında fırlatıldı ve dosya açık kaldı (`false`) ta ki elle `close` çağrılana kadar. Fark bloğun içeriğinde değil, kapatma sorumluluğunun yerindedir: bloklu `File.open`, bloğu bir `ensure` içinde koşturur ve ne olursa olsun dosyayı kapatır. Bu, önceki dersteki kaynak temizleme yapısının dilin kitaplığına yerleştirilmiş biçimidir. Sonucu blok belirler, kaynak temizliğini `File.open` üstlenir. **KB66.** Ölçüm `RuntimeError` ile yapıldı; blokta `break` ya da `throw` ile çıkış denenmedi.

#### Özet

- `File.read`, `File.readlines` ve `File.foreach` dosyayı kendileri açıp kapatır; `File.write` yazılan bayt sayısını döndürür; olmayan bir dosyayı okumak `Errno::ENOENT` fırlatır.
- Bu dersin yanıtlayanı **blok**tur: aynı `File.open` iletisi bir blokla `"ilk satir"`, öteki blokla `2` verdi.
- Bloksuz `File.open` dosyayı açık bırakır; bloklu çağrıda blok bitince `closed?` `true` olur ve dönüş değeri bloğun değeridir.
- `gets`, `each_line` ve `$stdout.puts` `IO` sınıfında tanımlıdır; standart girdi bir borudan beslenerek okunur.
- `"w"` dosyayı sıfırlar, `"a"` sonuna ekler; `puts` satır sonu ekler, `print` eklemez; yineleyicinin `map` ve `count` yöntemleri `Enumerable` modülündedir.
- Sınırlayıcı ölçüm: blok istisna fırlatsa da dosya kapanır, çünkü kapatmayı blok değil `File.open` yöntemi yapar.

#### Kurs Kapanışı

Kursun her dersi aynı soruyu sordu: bu iletiyi kim yanıtlıyor. Tablo, on iki dersin ölçtüğü iletiyi, o dersin yanıtlayanını ve sınırlayıcı ölçümünün söylediği şeyi yan yana koyar. Yanıtlayan sütunu her dersin `## Özet` bölümünde yazılı olandır.

| Ders | Ölçülen ileti | Yanıtlayan | Sınırlayıcı ölçüm |
|---|---|---|---|
| Ruby'nin Tasarım Felsefesi | `5.between?(1, 10)` | modül | `between?` `Comparable`'da, `<=>` alıcının sınıfında |
| Nesne Modeli Sezgisi | `5 + 3` | sınıf | `Integer.===` iletisini `Module` yanıtlar |
| Değişkenler ve Sabitler | `x = 5` | sözdizimi | `y += 1` atamadır, içindeki toplamayı `Integer` yanıtlar |
| Denetim Akışı | `(1..10) === 7` | sınıf | `case`, dal nesnesinin sınıfına sorar |
| Yöntemler | `format("%03d", 7)` | modül | üst düzey `def` `Object`'e yazılır ve özeldir |
| Semboller ve Dizgiler | `"ad".to_sym` | sınıf | sihirli yorum sahibi değiştirmez, dosya başına geçerlidir |
| Diziler ve Karma Yapılar | `Hash.new { 0 }[:x]` | blok | blok yalnız anahtar yokken yanıtlar |
| Bloklar | `sayilar.map { ... }` | blok | `each` iki blokla da `[1, 2]` verir, sahibi `Array` |
| `Enumerable` Modülü | `(1..5).each_slice(2)` | modül | `sum` aralıkta modülde, dizide `Array`'de |
| Proc ve Lambda | `->(n) { n * 2 }.call(3)` | sınıf | sahip aynı, fark `lambda?` durumunda |
| İstisnalar | `begin ... rescue ... end` | sözdizimi | dalın eşleşmesi `===` iletisiyle yapılır |
| Dosya ve Girdi/Çıktı | `File.open("not.txt") { ... }` | blok | kapatmayı blok değil yöntem yapar |

On iki dersin yanıtlayanı dağılımı: **4 sınıf · 3 modül · 3 blok · 2 sözdizimi**. Bu, çekirdeğin on iki gözleminin dağılımıyla aynıdır, çünkü çekirdek her ders için bir gözlem taşır. İki sonuç tablodan okunur. Birincisi, yanıtlayan çoğu zaman tek başına çalışmadı: sınırlayıcı ölçümlerin çoğu, dersin yanıtlayanının yanında başka bir yanıtlayanın da çalıştığını gösterdi. İkincisi, yanıtlayan sorusunun cevabı iletiye değil, iletinin alıcısına, verilen bloğa ve ifadenin biçimine bağlıdır. **KB67.** Tablo bu kursun on iki gözleminin dağılımını verir; Ruby'nin tamamının dağılımı hakkında bir iddia taşımaz.

Kurs burada kapanır. Ruby'de bir işlemin sonucunu üreten kod dört yerde durur: alıcının sınıfında, karıştırılmış bir modülde, çağıranın verdiği blokta ya da dilin kendi sözdiziminde. Bu kurs bu dört yeri yalnız **sonucu** ölçerek ayırdı; bir yöntemin neden belirli bir sınıfta ya da modülde bulunduğu sorusu açık kaldı. Sıradaki kursun konu alanı sınıf tanımı, kalıtım, modüller ve karışımlar, yöntem arama zinciri ve metaprogramlamadır; bu derslerde bugünkü ölçümlerin arkasındaki düzenek ele alınacaktır.

