D.ershane D Programlama Dili Dersleri

değişken: [variable], kavramları temsil eden veya sınıf nesnesine erişim sağlayan program yapısı
değişmez: [immutable], programın çalışması süresince kesinlikle değişmeyen
işlev: [function], programdaki bir kaç adımı bir araya getiren program parçası
kapsam: [scope], küme parantezleriyle belirlenen bir alan
parametre: [parameter], işleve işini yapması için verilen bilgi
sabit: [const], bir bağlamda değiştirilmeyen
takma isim: [alias], türün başka bir ismi
... bütün sözlük

Bölümler
İngilizce Kaynaklar
Diğer



const ve immutable

Ddili'nin makaleler bölümünde bu konuyu anlatan "const ve immutable Kavramları" isimli bir çeviri var. O makale, bu kavramları başka programlama dillerinden, ve özellikle C++'dan tanıyan kişilere yönelik olarak yazılmış. Eğer böyle bir deneyiminiz varsa, o makale sizin için yeterli olabilir. Ben bu derste bu kavramları yalnızca şimdiye kadar öğrendiklerimize bağlı olarak göstereceğim.

Değişkenler, programda geçen kavramları temsil ederler. Somut veya soyut, her türlü kavramı bir değişken olarak tanımlarız. Temsil ettikleri kavramlar arasındaki etkileşimleri de bu değişkenlerin değerlerini değiştirerek sağlarız:

    // Parayı öde
    toplamFiyat = fiyatıHesapla(fiyatListesi);
    cüzdandakiMiktar -= toplamFiyat;
    bakkaldakiMiktar += toplamFiyat;

Bu açıdan bakınca, değişkenler olmadan olmaz: değişebilme kavramı, programların herhangi bir iş yapabilmeleri için kesinlikle gereklidir.

Buna rağmen, bu esnekliğin uygun olmadığı durumlar da vardır:

D'deki değişmezlik kavramlarını gösteren iki anahtar sözcük İngilizce'de de çok yakın anlamdadırlar: const, "sabit, değişmez" anlamına gelen "constant"ın kısasıdır. immutable ise, "değişebilen" anlamına gelen "mutable"ın karşıt anlamlısıdır. Sonuçta ikisi de "değişmez" anlamına geliyor olsalar da, programda görevleri farklıdır ve bazı durumlarda birbirleriyle uyumsuzdurlar.

Bu uyumsuzluğa önceki derslerde rastladık: kendisi aslında immutable(char)[]'ın takma ismi olan string, değişebilen char[] dizgileriyle uyumlu değildir.

const

Bu anahtar sözcük bir değişkene sabitmiş gibi davranılacağını, yani o değişkenin değiştirilmeyeceğini belirler. Tanımlandığı sırada tür isminden önce const yazılan değişkenler, ilk değerleri verildikten sonra artık değiştirilemezler.

Şimdiye kadar hep değiştirilebilen kavramlar gördük:

    int birDeğer = birHesap();
    // ... daha sonra:
    birDeğer = başkaHesap();   // ← çalışır; değişebilir

Aynı kavramı const olarak tanımlamak, derleyicinin onun değiştirilmesine izin vermemesini sağlar:

    const int birDeğer = birHesap();
    // ... daha sonra:
    birDeğer = başkaHesap();   // ← derleme HATASI

Bu, programcı için yararlı bir kısıtlamadır. Değişkenin belirli bir kapsam içinde değişmeyecek olduğunu bilmek, kodun anlaşılmasını çok kolaylaştırır. Olası hataları da önler: değişmeyecek olduğu düşünüldüğü için const olarak belirlenmiş olan bir değişkeni sonradan değiştirmeye çalışmaya derleyici izin vermez. O noktada programcı ya yanlışlığı farkeder, ya da tasarımı gözden geçirerek const'ın kaldırılması gerektiğine karar verir.

Bir örnek olarak, bir fiyatıHesapla işlevine bakalım. Bu işlev, fiyat listesi olarak bir double dizisi alıyor ve dizideki bütün fiyatların toplamlarını döndürüyor olsun:

import std.cstream;

double fiyatıHesapla(double[] fiyatListesi)
{
    double toplam = 0;

    foreach (birimFiyatı; fiyatListesi) {
        toplam += birimFiyatı;
    }

    return toplam;
}

void main()
{
    double[] fiyatListesi = [ 10.25, 5.50, 3.00 ];
    // ... sonra başka fiyat da eklenebilir

    double toplamFiyat = fiyatıHesapla(fiyatListesi);
    dout.writefln("Toplam fiyat: ", toplamFiyat);
}

fiyatıHesapla işlevinin, kendisine verilen fiyat listesinde bir değişiklik yapması mantıklı olmaz. Onun işi, listedeki birimlerin fiyatlarını ekleyerek toplam fiyatı döndürmek olmalıdır. Oysa, parametresini const olarak almadığı için, fiyatListesi'nin değişmesi için bir engel yoktur. Belki de bir yanlışlık sonucu olarak eklenebilecek bir satır, fiyatListesi'nin değişmesine neden olur:

double fiyatıHesapla(double[] fiyatListesi)
{
    fiyatListesi[0] = 1234.56;   // ← sorunsuz derlenir

fiyatıHesapla işlevini çağıran kapsamlar, verilen parametrenin değişmediğinden emin olamazlar. Bu, programcının aklında tutması gereken bir bilgidir: bugün değiştirmiyorsa bile, ilerideki bir kod geliştirme sonucunda parametre değişebilir.

Bu sakıncayı ortadan kaldırmak için, parametrenin değişmeyeceğini const anahtar sözcüğü ile belirtmek gerekir. Böylece parametrenin değişmesini hem derleyici önleyecektir, hem de programcılar işleve verilen parametrenin değişmediğinden emin olabileceklerdir. Bu kod artık derlenemez:

double fiyatıHesapla(const double[] fiyatListesi)
{
    fiyatListesi[0] = 1234.56;   // ← derleme HATASI

Bu yararlı bir denetimdir. Fiyat hesaplayan bir işlevin listede değişiklik yapması olsa olsa bir hata olacağı için; derleyicinin sağladığı bu güvenceden yararlanmak önemlidir.

Devam edersek; eğer aynı örnekteki toplamFiyat'ın bir kere ilklendikten sonra bir daha değişmeyeceği düşünülüyorsa, o da const olarak tanımlanabilir:

    const double toplamFiyat = fiyatıHesapla(fiyatListesi);

toplamFiyat da artık değiştirilemez.

const değişken, const olmayan parametre yerine kullanılamaz

Değişmeyecek olduğu halde const olarak tanımlanmayan bir parametre, işlevin kullanışlılığını düşürür. Bunu görmek için tekrar fiyatıHesapla işlevine dönelim ve parametresinin en baştaki gibi yanlış olarak const olmadan tanımlandığını varsayalım:

double fiyatıHesapla(double[] fiyatListesi)
{

O işlev, programın ilk gösterdiğim halinde de zaten öyleydi. O haliyle derlenir ve program doğru olarak çalışır.

Şimdi o işlevin çağrılmasından hemen önceye dönelim. main içindeki fiyatListesi'nin değişmeyecek olduğunu, ve bu durumda programcının onu doğru olarak const ile tanımladığını varsayalım:

    const double[] fiyatListesi = [ 10.25, 5.50, 3.00 ];
    // ... artık başka fiyat eklenemez

    double toplamFiyat =
             fiyatıHesapla(fiyatListesi); // ← derleme HATASI

Program artık derlenemez. main içinde const olarak tanımlanan, yani değişmeyecek olan bir dizinin, onu değiştirmeyeceği güvencesini vermeyen bir işleve parametre olarak gönderilmesine derleyici izin vermez. Sonuçta, işlev parametresinin const olarak tanımlanmamış olması, işlevin kullanışlılığını düşürmektedir, ve işlevin ancak const olmayan dizilerle kullanılabilmesine neden olmaktadır.

const'ın, mantıklı olan her yerde kullanılması gerekir. const'ın yalnızca programcıya yardımcı bir olanak olarak değil, programın tutarlılığı açısından dikkat edilmesi gereken bir olgu olarak kabul edilmesi gerekir.

Not: const'ın doğru olarak kullanılmasıyla ilgili olan bu konunun İngilizce'si "const correctness"tır.

const değişken, kesinlikle değişmez değildir

const, değişkenin "bu kapsam içinde" veya "bu parametre yoluyla" değiştirilemeyeceğini belirler. Oysa değişkenin kendisi değişmez olmayabilir. Bunu yukarıdaki fiyatıHesapla işlevinin parametresinde görmüştük: değişken, parametre const olduğu için, işlev içinde değiştirilemiyordu; oysa main içindeki fiyatListesi yine de değiştirilebiliyordu.

Bunu değişik bir örnekle tekrar görelim. Herhangi bir değişkeni çıkışa yazdırmak için kullanılacak olan bir işlevin, yazdıracağı parametrede değişiklik yapmasını beklemeyiz. Yoksa hem sürprizlerle karşılaşırız, hem de son gördüğümüz derleme hatasında olduğu gibi, const değişkenleri o işlevle yazdıramayız. Bu yüzden, bir öğrencinin notlarını yazdıran bir işlevin parametrelerini const olarak tanımlamak doğru olur:

void notlarıYazdır(const char[] isim, const int[] notlar)
{
    dout.writefln("Öğrenci: ", isim);

    dout.writef("Notlar :");
    foreach (not; notlar) {
        dout.writef(' ', not);
    }

    dout.writefln();
}

Oysa örneğin notlar, o işlevi çağıran tarafta değişebiliyor olabilir:

    int[] notlar = [ 80, 95, 90 ];
    notlarıYazdır(isim, notlar);

    notlar[0] = 81;
    notlarıYazdır(isim, notlar);
Öğrenci: Mehmet
Notlar : 80 95 90
Öğrenci: Mehmet
Notlar : 81 95 90

Özetle, const, bir değişkenin "burada değişmeyeceği" veya "bu isimle değişmeyeceği" gibi bir anlam taşır. notlarıYazdır işlevinin parametresi const olduğu için notlar orada değiştirilemez; ama işlevin çağrıldığı kapsamda int[] olarak tanımlandığı için değişebilir.

immutable

Bu anahtar sözcük, değişkenin değişmezliği konusunda const'tan daha kuvvetli bir anlama sahiptir. const'tan farklı olarak, bir değişkenin programın çalışması sırasında kesinlikle değişmeyeceğini bildirir.

Örneğin haftanın gün isimlerini taşıyan bir dizi hiçbir zaman değişmez; kullanıcıya üç deneme şansı veren bir oyunda deneme sayısı sınırı değişmez; programın gösterdiği menü seçenekleri değişmezdir, vs. Bu tür değişkenler immutable ile tanımlanırlar:

    immutable string günİsimleri[] =
    [
        "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma",
        "Cumartesi", "Pazar"
    ];
    günİsimleri[0] = "İlk gün";     // ← derleme HATASI

    immutable int denemeSayısıSınırı = 3;
    denemeSayısıSınırı = 4;         // ← derleme HATASI
const değişken, immutable parametre yerine kullanılamaz

Her ikisi de değişmezliği belirliyor olsalar da, const değişkenler işlevlere immutable parametre olarak gönderilemezler:

void immutableAlanİşlev(immutable int[] birDizi)
{
    // ...
}

void main()
{
        const int[] constDizi;
    immutable int[] immutableDizi;

    immutableAlanİşlev(constDizi);        // ← derleme HATASI
    immutableAlanİşlev(immutableDizi);    // ← çalışır
}

Bu konunun benzeriyle daha önceki derslerde de karşılaştık. Hatırlarsanız, dizgi türlerinin takma isimlerinin asıl türlerinin immutable olduklarından bahsetmiştik:

Ek olarak, dizgi hazır değerleri de değişmezdirler:

.dup ve .idup

Bu yüzden, dizgileri işlevlere parametre olarak geçirirken uyumsuz durumlarla karşılaşabiliriz. Bu durumlarda dizgilerin .dup ve .idup niteliklerinden yararlanmamız gerekebilir:

Örneğin, değişebilen bir dizgi alan bir işlevi bir string ile doğrudan çağıramayız. Bu durumda işlevi .dup ile aldığımız kopya ile çağırmamız gerekir:

void foo(char[] dizgi)
{
    // ...
}

void main()
{
    foo("merhaba");            // ← derleme HATASI
    foo("merhaba".dup);        // ← çalışır
}

Parametresinin programın çalışması süresince kesinlikle değişmeyecek olmasını isteyen ve bu yüzden de onu immutable olarak belirlemiş olan bir işlevi de .idup ile çağırmamız gerekebilir:

void foo(string dizgi)
{
    // ...
}

void main()
{
    char[] selam;
    foo(selam);                // ← derleme HATASI
    foo(selam.idup);           // ← çalışır
}
Nasıl kullanmalı

Değişken tanımlarken, programın çalışması sırasında kesinlikle değişmeyecek olan değerleri immutable olarak tanımlayın. Örneğin bir dakikadaki toplam saniye sayısı değişmeyecektir:

    immutable int dakikaBaşınaSaniye = 60;

Parametre tanımlarken; eğer parametrede bir değişiklik yapılmayacaksa, parametreyi const olarak tanımlayın. Öyle yaptığınızda parametreyi değiştirmeme sözü verdiğiniz için; değişebilen, const, ve immutable değişkenleri o işleve gönderebilirsiniz:

void foo(const char[] dizgi)
{
    // ...
}

void main()
{
    char[] değişebilenDizgi;
    const char[] constDizgi;
    string immutableDizgi;

    foo(değişebilenDizgi);  // ← çalışır
    foo(constDizgi);        // ← çalışır
    foo(immutableDizgi);    // ← çalışır
}

Eğer parametrede bir değişiklik yapacaksanız, o parametreyi değişebilen şekilde tanımlayın; zaten const veya immutable tanımladığınızda değiştiremezsiniz:

import std.cstream;

void tersÇevir(char[] dizgi)
{
    const int uzunluk = dizgi.length;

    for (int i = 0; i != uzunluk / 2; ++i) {
        const char geçici = dizgi[i];
        dizgi[i] = dizgi[uzunluk - 1 - i];
        dizgi[uzunluk - 1 - i] = geçici;
    }
}

void main()
{
    char[] selam = "merhaba".dup;
    tersÇevir(selam);
    dout.writefln(selam);
}
abahrem

Genel bir kural olarak, olabildiği kadarıyla hep const tarafta kalmaya çalışın. Örneğin işlevin içinde değiştirilmeyen parametreleri const olarak tanımlayın.

Geçerli bir nedeni yoksa, işlev parametrelerinde immutable kullanmayın. Yoksa işlevinizin kullanışlılığı düşer.

Bundan sonraki bölümlerdeki örneklerde ben de bol bol const ve immutable kullanacağım.