D Programlama Dili – Programlama dersleri ve D referansı
Ali Çehreli

deyim: [statement], ifadelerin işletilmelerini ve sıralarını etkileyen program yapısı
kapsam: [scope], küme parantezleriyle belirlenen bir alan
üçlü işleç: [ternary operator], ifadenin değerine göre ya birinci, ya da ikinci değeri üreten işleç
... bütün sözlük



İngilizce Kaynaklar


Diğer




switch ve case

switch, çoklu koşul gibi çalışan bir deyimdir ve bu açıdan bir "if else if" zincirine benzer. Buradaki kullanımında "durum" anlamına gelen case, switch'in denetlediği değerin karşılaştırıldığı durumları belirlemek için kullanılır; kendisi bir deyim değildir.

switch, parantez içinde bir ifade alır; o ifadenin değerini kendi kapsamı içindeki case'lerle karşılaştırır ve o değere eşit olan case'in işlemlerini işletir. Söz dizimini şöyle gösterebiliriz:

    switch (ifade) {

    case değer_1:
        // ifade'nin değer_1'e eşit olduğu durumdaki işlemler
        // ...
        break;

    case değer_2:
        // ifade'nin değer_2'ye eşit olduğu durumdaki işlemler
        // ...
        break;

    // ... başka case'ler ...

    default:
        // hiçbir değere uymayan durumdaki işlemler
        // ...
        break;
    }

Her ne kadar bir koşul gibi çalışsa da, switch'in aldığı ifade bir mantıksal ifade olarak kullanılmaz. Yani bir if'te olduğu gibi "eğer böyleyse" anlamında değildir. switch'teki ifadenin değerinin, case'lerdeki değerlere eşit olup olmadığına bakılır. Yani, buradaki koşullar hep eşitlik karşılaştırmalarıdır. Bu açıdan bakıldığında bir "if else if" zinciri gibi düşünülebilir:

    auto değer = ifade;

    if (değer == değer_1) {
        // değer_1 durumundaki işlemler
        // ...

    } else if (değer == değer_2) {
        // değer_2 durumundaki işlemler
        // ...
    }

    // ... başka 'else if'ler ...

    } else {
        // hiçbir değere uymayan durumdaki işlemler
        // ...
    }

Ancak, bu "if else if" switch'in tam eşdeğeri değildir. Nedenlerini aşağıdaki başlıklarda açıklıyorum.

İfadenin değerine eşit olan bir case değeri varsa, o case'in altındaki işlemler işletilir. Eğer yoksa, "varsayılan" anlamına gelen default'un altındaki işlemler işletilir.

goto

goto programcılıkta kaçınılması öğütlenen bir deyimdir. Buna rağmen nadir durumlarda switch deyimi ile kullanılması gerekebilir. goto deyimini ayrıntılı olarak daha ilerideki bir bölümde göreceğiz.

if koşulunun kapsamı olduğu için, kapsamdaki işlemler sonlanınca bütün if deyiminin işi bitmiş olur. switch'te ise ifadenin değerine eşit bir case bulunduğu zaman programın işleyişi o case'e atlar ve ya bir break ile ya da bir goto case ile karşılaşılana kadar devam eder. goto case hemen alttaki case'e devam edilmesine neden olur:

    switch (değer) {

    case 5:
        writeln("beş");
        goto case;    // bir sonraki case'e devam eder

    case 4:
        writeln("dört");
        break;

    default:
        writeln("bilmiyorum");
        break;
    }

goto case'in bu kullanımı isteğe bağlıdır çünkü break deyimi bulunmadığında program zaten bir sonraki case veya default bölümüne devam eder:

    case 5:
        writeln("beş");
        // 'break' deyimi yok; bir sonraki case'e devam eder

    case 4:
        writeln("dört");
        break;

değer 5 olduğunda case 5 satırının altına gidilir ve orada "beş" yazdırılır. Onun sonundaki goto case bir sonraki case'e devam edilmesini sağladığı için "dört" de yazdırılır ve çıktıda ikisi de yer alırlar:

beş
dört

goto deyimi case bölümlerinde üç farklı biçimde kullanılabilir:

Bu üç kullanımı bir önceki bölümde gördüğümüz foreach'ten de yararlanan aşağıdaki programla deneyebiliriz:

import std.stdio;

void main() {
    foreach (değer; [ 1, 2, 3, 10, 20 ]) {
        writefln("--- değer: %s ---", değer);

        switch (değer) {

        case 1:
            writeln("case 1");
            goto case;

        case 2:
            writeln("case 2");
            goto case 10;

        case 3:
            writeln("case 3");
            goto default;

        case 10:
            writeln("case 10");
            break;

        default:
            writeln("default");
            break;
        }
    }
}

Çıktısı:

--- değer: 1 ---
case 1
case 2
case 10
--- değer: 2 ---
case 2
case 10
--- değer: 3 ---
case 3
default
--- değer: 10 ---
case 10
--- değer: 20 ---
default
İfadenin değeri ancak tamsayı, bool, veya dizgi olabilir

if'te eşitlik karşılaştırmasında herhangi bir tür kullanılabilir. switch'te ise ifade değeri olarak ancak tamsayılar, bool, veya dizgiler kullanılabilir.

    string işlem = /* ... */;
    // ...
    switch (işlem) {

    case "toplama":
        sonuç = birinci + ikinci;
        break;

    case "çıkarma":
        sonuç = birinci - ikinci;
        break;

    case "çarpma":
        sonuç = birinci * ikinci;
        break;

    case "bölme":
        sonuç = birinci / ikinci;
        break;

    default:
        throw new Exception(format("Geçersiz işlem: %s", işlem));
    }

Not: Yukarıdaki kod hiçbir case'e uymayan durumda bir hata atmaktadır. Hataları ilerideki bir bölümde göreceğiz.

Her ne kadar ifade türü olarak bool da kullanılabiliyor olsa da, false ve true diye iki değeri olan bu tür için çoğu durumda if'in veya ?: üçlü işlecinin daha uygun olduğunu düşünebilirsiniz.

Değer aralıkları

Belirli bir değer aralığındaki durumlar case'ler arasına .. karakterleri yerleştirilerek belirtilir:

    switch (zarDeğeri) {

    case 1:
        writeln("Sen kazandın");
        break;

    case 2: .. case 5:
        writeln("Berabere");
        break;

    case 6:
        writeln("Ben kazandım");
        break;

    default:
        /* Aslında bu durumun hiç gerçekleşmemesi gerekir çünkü
         * yukarıdaki durumlar bütün olası değerleri
         * kapsamaktadır. (Aşağıdaki 'final switch'e bakınız.) */
        break;
    }

Yukarıdaki zarla oynanan oyunda zarın 2, 3, 4, veya 5 değerinde berabere kalınmaktadır.

Ayrık değerler

Yukarıdaki oyunda [2,5] aralığında değil de 2 ve 4 değerleri geldiğinde berabere kalındığını varsayalım. Öyle durumlarda case'in değerlerinin aralarına virgül yazılır:

    case 2, 4:
        writeln("Berabere");
        break;
final switch deyimi

Bu deyim de switch gibidir ama bazı kısıtlamaları vardır:

    final switch (zarDeğeri) {

    case 1:
        writeln("Sen kazandın");
        break;

    case 2, 3, 4, 5:
        writeln("Berabere");
        break;

    case 6:
        writeln("Ben kazandım");
        break;
    }
Ne zaman kullanmalı

Yukarıda anlatılanlardan anlaşıldığı gibi; switch, bir ifadenin derleme zamanında bilinen değerlerle karşılaştırıldığı durumlarda kullanışlıdır.

Eğer karşılaştırılacak değer yalnızca iki taneyse, switch yerine bir "if else" daha uygun olabilir. Örneğin yazı/tura gibi bir sonuçta if deyimi yeterlidir:

    if (yazıTuraSonucu == yazı) {
        // ...

    } else {
        // ...
    }

Genel bir kural olarak, switch'i üç veya daha fazla değer olduğunda düşünebilirsiniz.

Mevcut değerlerin her birisinin case değeri olarak yer alması gereken durumlarda final switch'i yeğleyin. Bu, özellikle enum türlerine uygundur.

Problemler
  1. Yukarıdaki örneklerden birisindeki gibi bir hesap makinesi yapın. Kullanıcıdan önce işlemi string olarak, sonra da sayıları double olarak alsın ve işleme göre hesap yapsın. Örneğin işlem "topla" olarak ve sayılar "5 7" olarak girildiğinde ekrana 12 yazsın.

    Girişi şu şekilde okuyabilirsiniz:

        string işlem;
        double birinci;
        double ikinci;
    
        // ...
    
        işlem = strip(readln());
        readf(" %s %s", &birinci, &ikinci);
    
  2. Hesap makinesini geliştirin ve "topla" gibi sözlü işlemler yanında "+" gibi simgeleri de desteklemesini sağlayın: işlem dizgisi olarak "+" girildiğinde de aynı şekilde çalışsın.
  3. Program bilinmeyen bir işlem girildiğinde hata atsın. Hata atma düzeneğini ilerideki bir bölümde göreceğiz. Şimdilik yukarıdaki throw deyimini kendi programınıza uygulayın.