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.
Dizi ve eşleme tabloları da derleme zamanı sabitleri olarak tanımlanabilirler. Ancak, daha sonra Değişmezlik bölümünde göreceğimiz gibi, enum
dizi ve eşleme tablolarının hız kayıban neden olabilen gizli bedelleri vardır.
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:
- Hangi işlem olduğunu sihirli sabitlerden değil,
enum
değerlerden anlasın. int
yerinedouble
kullansın.- "if else if" zinciri yerine
switch
kullansın.