D.ershane D Programlama Dili Dersleri

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

Bölümler
İ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 (kullanmayın)

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.

Sihirli sabitler programcılıkta kaçınılan bir durumdur; çünkü 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 derslerdeki 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, en basit olarak şu söz dizimine sahiptir:

    enum Türİsmi { 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 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ç;

Türün değerleri kod içinde sabit olarak yazıldıklarında, 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ğerleri

enum türlerin değerleri, normalde arka planda yine de int olarak kullanılırlar. Yani her ne kadar Yazı ve Tura gibi isimleri olsa da, yine de arka planda birer int değeridirler. (Not: Aslında int yerine başka bir tür kullanmanın da yolu vardır.)

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ğerini yazdırdığımızda 0 ve 1 değerlerine sahip olduklarını görürüz:

    writeln("Yazı: ", ParaAtışıSonucu.Yazı);
    writeln("Tura: ", ParaAtışıSonucu.Tura);
Yazı: 0
Tura: 1

Normalde 0'dan başlayan bu değerleri istediğimiz noktadan itibaren = işareti ile kendimiz belirleyebiliriz. (Yukarıda bunu İşlem'in Toplama değerinde de görmüş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, ğ }
    writeln(Deneme.b, ' ', Deneme.ç, ' ', Deneme.ğ);
1 100 224

Gerçek hayattan bir enum örneği olarak dosyalar dersinde gördüğümüz std.stream.File'ın dosya erişim haklarına bakabiliriz. FileMode türü, std/stream.d dosyasında şöyle tanımlanmıştır:

enum FileMode {
  In = 1,
  Out = 2,
  OutNew = 6,
  Append = 10
}
Türsüz enum'lar

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

Ancak, enum kullanıldı diye sabit değerlere ayrıca tür ismi vermek 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 bir sabitin tanımlanmasında ayrıca enum türü belirlemeye gerek yoktur. Böyle durumlarda tür ismi 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;
Dizgi karşılıkları

enum'lar arka planda tamsayı oldukları için, çıkışa normalde tamsayı olarak yazdırılırlar. Bunu yukarıda Yazı ve Tura için 0 ve 1 olarak görmüştük.

Ek olarak, Phobos'un std.conv modülünün olanaklarından yararlanarak enum değerleri dizgi olarak yazdırmak da mümkündür. O modüldeki to şablonu, enum değerleri koddaki isimleriyle de yazdırabilir:

import std.conv;
// ...

    writeln(to!string(atışSonucu));

Oradaki atışSonucu'nun değerinin ParaAtışıSonucu.Yazı olduğunu varsayarsak, çıktısı:

Yazı

Not: Şablonları daha sonra göreceğiz. Şimdilik to!string(atışSonucu) kullanımını, "atışSonucu'nu string'e dönüştür" olarak düşünebilirsiniz.

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 döngüde 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) {

        writeln(to!string(renk), ": ", renk);
    }
Maça: 0
Kupa: 1
Karo: 2
Sinek: 3
int'ten dönüştürmek

Yukarıdaki yazdırma örneklerinde görüldüğü gibi, bir enum değer otomatik olarak int'e 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

Not: Tür dönüşümlerini de ileride göreceğiz.

Problem
  • Tamsayılar ve Aritmetik İşlemler dersinin 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:

    • int yerine double kullansın
    • işlemi sihirli sabitlerden değil, enum değerlerden anlasın
    • "if else if" zinciri yerine switch kullansın
... çözüm