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

sağ değer: [rvalue], adresi alınamayan değer
sihirli sabit: [magic constant], ne anlama geldiği anlaşılmayan sabit değer
şablon: [template], derleyicinin örneğin 'türden bağımsız programlama' için kod üretme düzeneği
... bütün sözlük



İngilizce Kaynaklar


Diğer




enum

enum, "numaralandırmak" anlamına gelen "enumerate"in kısaltılmışıdır. İsimli sabit değerler üretmek için kullanılır.

Sihirli sabitler

Tamsayılar ve Aritmetik İşlemler bölümünün problem çözümlerinden birisinde şöyle bir koşul kullanmıştık:

        if (işlem == 1) {
            sonuç = birinci + ikinci;

        } else if (işlem == 2) {
            sonuç = birinci - ikinci;

        } else if (işlem == 3) {
            sonuç = birinci * ikinci;

        } else if (işlem == 4) {
            sonuç = birinci / ikinci;
        }

O kod parçasındaki 1, 2, 3, ve 4 değerlerine sihirli sabit denir. Kodu okuyan birisinin onların ne anlama geldiklerini bir bakışta anlaması olanaksızdır. Örneğin yukarıdaki kodda 1'in toplama işlemi, 2'nin çıkarma işlemi, vs. anlamlarına geldiklerini ancak kapsamlarındaki kodları okuduktan sonra anlayabiliyoruz. Bu durumda şanslıyız, çünkü her kapsamda yalnızca tek satır var; daha karmaşık kodlarda kodu anlamak çok güç olabilir.

Programcılıkta sihirli sabitlerden kaçınılır çünkü onlar iyi yazılmış kodun en önemli niteliklerinden olan okunurluğunu azaltırlar.

enum olanağı işte bu tür sabitlere isimler vermeyi ve bu sayede kodun okunurluğunu arttırmayı sağlar. Aynı kod enum değerleriyle yazıldığında her bir if koşulunun hangi işlemle ilgili olduğu açıkça anlaşılır:

        if (işlem == İşlem.toplama) {
            sonuç = birinci + ikinci;

        } else if (işlem == İşlem.çıkarma) {
            sonuç = birinci - ikinci;

        } else if (işlem == İşlem.çarpma) {
            sonuç = birinci * ikinci;

        } else if (işlem == İşlem.bölme) {
            sonuç = birinci / ikinci;

        }

Artık 1 gibi anlamı açık olmayan bir değer yerine İşlem.toplama gibi isimli bir değer kullanılmaktadır. Bundan sonraki bölümlerdeki kodlarda sihirli sabitler yerine hep isimli sabitler kullanacağım.

Yukarıdaki 1, 2, 3, ve 4 değerlerine karşılık gelen enum tanımı şöyle yazılır:

    enum İşlem { toplama = 1, çıkarma, çarpma, bölme }
Söz dizimi

enum yaygın olarak şu söz dizimiyle kullanılır:

    enum Türİsmi { değerİsmi_1, değerİsmi_2, /* vs. */ }

Bazen değerlerin asıl türlerini de belirtmek gerekebilir. Bunun nasıl kullanıldığını bir sonraki başlıkta göreceğiz:

    enum Türİsmi : asıl_tür { değerİsmi_1, değerİsmi_2, /* vs. */ }

enum anahtar sözcüğünden sonra bütün değerlerin toplu olarak ne anlama geldiğini belirten bir tür ismi verilir. Bütün olası değerler isimler halinde enum kapsamı içinde sıralanırlar.

Bir kaç örnek:

    enum ParaAtışıSonucu { yazı, tura }
    enum OyunKağıdıRengi { maça, kupa, karo, sinek }
    enum BiletTürü { normal, çocuk, öğrenci, emekli }

Bu değerler aynı zamanda yeni bir türün parçaları haline de gelirler. Örneğin yazı ve tura artık ParaAtışıSonucu diye tanımlanmış olan yeni bir türün değerleridir. Bu yeni tür de başka türler gibi değişken tanımlamak için kullanılabilir:

    ParaAtışıSonucu sonuç;           // otomatik ilklenerek
    auto yt = ParaAtışıSonucu.yazı;  // türü çıkarsanarak

Yukarıdaki kodlarda da olduğu gibi, enum türlerinin değerleri kod içinde sabit olarak belirtilecekleri zaman ait oldukları türün ismiyle birlikte ve ondan bir nokta ile ayrılarak yazılırlar:

    if (sonuç == ParaAtışıSonucu.yazı) {
        // ...
    }
Asıl değerler ve türleri

enum türlerin değerleri arka planda normalde int olarak gerçekleştirilirler. Yani her ne kadar yazı ve tura gibi isimleri olsa da, arka planda birer int değeridirler. (int'ten başka türlerin de kullanılabileceğini aşağıda göreceğiz.)

Bu değerler programcı özellikle belirtmediği sürece 0'dan başlar ve her isimli değer için bir tane arttırılır. Örneğin yukarıda tanımlanan ParaAtışıSonucu'nun iki değerinin sırasıyla 0 ve 1'e eşit olduklarını şöyle gösterebiliriz:

    writefln("yazı'nın değeri 0: %s", (ParaAtışıSonucu.yazı == 0));
    writefln("tura'nın değeri 1: %s", (ParaAtışıSonucu.tura == 1));

Çıktısı:

yazı'nın değeri 0: true
tura'nın değeri 1: true

Normalde 0'dan başlayan bu değerleri istediğimiz noktadan itibaren = işareti ile kendimiz de belirleyebiliriz. Yukarıda İşlem.toplama değerini 1 olarak belirken bundan yararlanmıştık. Belirlediğimiz değerden sonrakilerin değerleri de yine derleyici tarafından birer birer arttırılarak verilir:

    enum Deneme { a, b, c, ç = 100, d, e, f = 222, g, ğ }
    writefln("%d %d %d", Deneme.b, Deneme.ç, Deneme.ğ);

Çıktısı:

1 100 224

enum değerlerinin perde arkasında tamsayılardan başka bir tür olması gerektiğinde o tür enum isminden sonra belirtilir:

    enum DoğalSabit : double { pi = 3.14, e = 2.72 }
    enum IsıBirimi : string { C = "Celcius", F = "Fahrenheit" }
Bir enum türüne ait olmayan enum değerleri

Sihirli sabitlerden kurtulmanın önemli olduğunu ve bu amaçla enum'lardan yararlanabileceğimizi gördük.

Ancak, sihirli sabitlerden kurtulabilmek için ayrıca bir enum türü belirlemek doğal olmayabilir. Örneğin tek amacımızın 24 saatteki toplam saniye sayısını tutan bir sabit tanımlamak olduğunu düşünelim. Böyle tek sabitin tanımlanmasında ayrıca enum türü belirlemeye gerek yoktur. Böyle durumlarda enum türü ve enum kapsam parantezleri yazılmayabilir:

    enum günBaşınaSaniye = 60 * 60 * 24;

Artık o sabiti hesaplarda ismiyle kullanabiliriz:

    toplamSaniye = günAdedi * günBaşınaSaniye;

enum, başka türden hazır değerler tanımlamak için de kullanılabilir. Örneğin isimli bir string hazır değeri şöyle tanımlanabilir:

    enum dosyaİsmi = "liste.txt";

Böyle sabitler sağ değerdirler ve İngilizce'de "manifest constant" diye anılırlar.

Nitelikleri

.min ve .max nitelikleri enum türünün sırasıyla en küçük ve en büyük değerleridir. Bunları bir for döngüsünde kullanarak bütün değerleri sırayla gezebiliriz:

    enum OyunKağıdıRengi { maça, kupa, karo, sinek }

    for (auto renk = OyunKağıdıRengi.min;
         renk <= OyunKağıdıRengi.max;
         ++renk) {

        writefln("%s: %d", renk, renk);
    }

"%s" ve "%d" düzen belirteçlerinin çıktılarının farklı olduklarına dikkat edin:

maça: 0
kupa: 1
karo: 2
sinek: 3

Bunun için foreach döngüsünün uygun olmadığına dikkat edin. foreach değer aralığı ile kullanılsaydı .max değeri aralığın dışında kalırdı:

    foreach (renk; OyunKağıdıRengi.min .. OyunKağıdıRengi.max) {
        writefln("%s: %d", renk, renk);
    }

Çıktısı:

maça: 0
kupa: 1
karo: 2
          ← sinek eksik

Bu yüzden, bir enum'ın bütün değerleri üzerinde ilerlemenin doğru bir yolu std.traits modülünde tanımlı olan EnumMembers şablonundan yararlanmaktır:

import std.traits;
// ...
    foreach (renk; EnumMembers!OyunKağıdıRengi) {
        writefln("%s: %d", renk, renk);
    }

Not: Yukarıdaki ! karakteri şablon parametre değeri bildirmek içindir. Şablonları ilerideki bir bölümde göreceğiz.

maça: 0
kupa: 1
karo: 2
sinek: 3  ← sinek mevcut
Asıl türden dönüştürmek

Yukarıdaki yazdırma örneklerinde görüldüğü gibi, bir enum değer perde arkasında kullanılan asıl türe (örneğin int'e) otomatik olarak dönüşür. Bunun tersi doğru değildir:

    OyunKağıdıRengi renk = 1;      // ← derleme HATASI

Bunun nedeni, enum değişkenlerine yanlışlıkla geçersiz değerlerin atanmasını önlemektir:

    renk = 100;   // ← geçerli bir değer olmadığı için
                  //   anlamsız olurdu

Geçerli olduğunu bildiğimiz bir değeri bir enum değerine dönüştürmek istiyorsak, bunu açıkça bir tür dönüşümü olarak yazmamız gerekir:

    renk = cast(OyunKağıdıRengi)1;    // şimdi kupa

Tür dönüşümlerini ilerideki bir bölümde göreceğiz.

Problem

Tamsayılar ve Aritmetik İşlemler bölümünün problemlerindeki hesap makinesini değiştirin: Dört işlemi destekleyen basit bir hesap makinesi, işlemi bir menüden seçtirsin ve girilen iki değere o işlemi uygulasın.

Programı bu sefer şu farklarla yazın: