D.ershane D Programlama Dili Dersleri

çökme: [crash], programın hata ile sonlanması
değişken: [variable], kavramları temsil eden veya sınıf nesnesine erişim sağlayan program yapısı
eşleme tablosu: [associative array], elemanlarına tamsayı olmayan indekslerle de erişilebilen veri yapısı (bir 'hash table' gerçekleştirmesi)
indeks: [index], topluluk elemanlarına erişmek için kullanılan bilgi
işleç: [operator], bir veya daha fazla değişkenle iş yapan özel işaret (||, &&, +, -, =, vs.)
nesne: [object], belirli bir sınıf veya yapı türünden olan değişken
sıradüzen: [hierarchy], sınıfların türeyerek oluşturdukları aile ağacı
üst sınıf: [super class], kendisinden sınıf türetilen sınıf
üye işlev: [member function], yapı veya sınıfın kendi tanımladığı işlemleri
... bütün sözlük

Bölümler
İngilizce Kaynaklar
Diğer



Object

Açıkça başka bir sınıftan türetilmeyen sınıflar otomatik olarak Object adlı sınıftan türerler.

Sıradüzenin en üstündeki sınıf Object'ten otomatik olarak türer:

class Çalgı : Object        // ": Object" yazılmaz; otomatiktir
{
    // ...
}

class TelliÇalgı : Çalgı    // dolaylı olarak Object'ten türer
{
    // ...
}

En üstteki sınıf Object'ten türediği için, altındaki bütün sınıflar da dolaylı olarak Object'ten türerler. Bu anlamda "her sınıf, Object türündendir".

Bu türeme sonucunda her sınıf Object'in bazı üye işlevlerini edinir:

Bu işlevlerden son üçü sınıf nesnelerinin değerlerini ön plana çıkartmak ve en azından onları eşleme tablolarında indeks türü olarak kullanmak için gereklidir.

Türeme yoluyla edinildikleri için, bu işlevlerin türeyen tür için override anahtar sözcüğü ile tanımlanmaları gerekir.

Not: Object'ten edinilen başka üyeler de vardır; onları burada anlatmayacağım.

toString

Yapılarda olduğu gibi, ve bir önceki derste de gördüğümüz gibi, nesnenin dizgi olarak kullanılabilmesini sağlar:

    auto saat = new Saat(20, 30, 0);
    writeln(saat);         // Object.toString'i çağırır

Sınıfın Object'ten kalıtım yoluyla edindiği toString işlevi fazla kullanışlı değildir; döndürdüğü string yalnızca türün ismini içerir:

deneme.Saat

Sınıfın isminden önceki bölüm, yani yukarıdaki deneme, o sınıfı içeren modülün ismini belirtir. Ona bakarak, Saat sınıfının deneme.d isimli bir kaynak dosya içinde tanımlandığını anlayabiliriz.

Daha anlamlı ve daha uygun bir string üretmesi için bu işlevi hemen hemen her zaman kendimiz tanımlamak isteriz:

import std.string;

class Saat
{
    override string toString() const
    {
        return format("%02s:%02s:%02s", saat, dakika, saniye);
    }

    // ...
}

class ÇalarSaat : Saat
{
    override string toString() const
    {
        return format("%s ♫%02s:%02s", super.toString(),
                      alarmSaati, alarmDakikası);
    }

    // ...
}

// ...

    auto başucuSaati = new ÇalarSaat(20, 30, 0, 7, 0);
    writeln(başucuSaati);

Yukarıdaki ÇalarSaat.toString; kendi üyelerini doğrudan kullanıyor ve üst sınıfın üyeleri için üst sınıfın toString işlevinden yararlanıyor. Çıktısı:

20:30:00 ♫07:00
opEquals

İşleç Yükleme dersinde gördüğümüz gibi, bu üye işlev == işlecinin tanımını belirler. İşlevin dönüş değeri nesneler eşitlerse true, değillerse false olmalıdır.

Uyarı: Bu işlevin opCmp ile tutarlı olması gerekir; true döndürdüğü durumda opCmp da sıfır döndürmelidir.

== işlecine karşılık öncelikle dört adımlı bir algoritma işletilir. Programcının tanımlamış olduğu opEquals ancak bazı durumlarda çağrılır:

bool opEquals(Object a, Object b) {
    if (a is b) return true;                          // (1)
    if (a is null || b is null) return false;         // (2)
    if (typeid(a) == typeid(b)) return a.opEquals(b); // (3)
    return a.opEquals(b) && b.opEquals(a);            // (4)
}
  1. İki değişken de aynı nesneye erişim sağlıyorlarsa (veya ikisi de null iseler) eşittirler.
  2. Yalnızca birisi null ise eşit değildirler.
  3. Her iki nesne de aynı türden iseler a.opEquals(b) işletilir.
  4. Aksi taktirde eşit olarak kabul edilebilmeleri için eğer tanımlanmışlarsa hem a.opEquals(b)'nin hem de b.opEquals(a)'nın true üretmesi gerekir.

opEquals programcı tarafından özellikle tanımlanmamışsa sınıf nesnelerinin değerlerine bakılmaz, iki sınıf değişkeninin aynı nesneye erişim sağlayıp sağlamadıklarına bakılır:

    auto değişken0 = new Saat(6, 7, 8);
    auto değişken1 = new Saat(6, 7, 8);

    assert(değişken0 != değişken1); // eşit değiller
                                    // (çünkü farklı nesneler)

Yukarıdaki koddaki iki nesne aynı parametrelerle kuruldukları halde, new ile ayrı ayrı kurulmuş oldukları için iki farklı nesnedir. Bu yüzden; onlara erişim sağlayan değişken0 ve değişken1 değişkenleri, Object'in gözünde eşit değillerdir.

Öte yandan, aynı nesneye erişim sağladıkları için şu iki değişken eşittir:

    auto ortak0 = new Saat(9, 10, 11);
    auto ortak1 = ortak0;

    assert(ortak0 == ortak1);       // eşitler
                                    // (çünkü aynı nesne)

Bazen nesneleri böyle kimliklerine göre değil, değerlerine göre karşılaştırmak isteriz. Örneğin değişken0'ın ve değişken1'in erişim sağladıkları nesnelerin değerlerinin eşit olmalarına bakarak, == işlecinin true üretmesini bekleyebiliriz.

Yapılardan farklı olarak, ve Object'ten kalıtımla edinildiği için, opEquals işlevinin parametresi Object'tir. O yüzden, bu işlevi kendi sınıfımız için tanımlarken parametresini Object olarak yazmamız gerekir:

class Saat
{
    override bool opEquals(Object o) const
    {
        // ...
    }

    // ...
}

Kendimiz Object olarak doğrudan kullanmayacağımız için bu parametrenin ismini kullanışsız olarak o diye seçmekte bir sakınca görmüyorum. İlk ve çoğu durumda da tek işimiz, onu bir tür dönüşümünde kullanmak olacak.

opEquals'a parametre olarak gelen nesne, kod içinde == işlecinin sağ tarafında yazılan nesnedir. Örneğin şu iki satır birbirinin eşdeğeridir:

    değişken0 == değişken1;
    değişken0.opEquals(değişken1);   // aynısı

Bu işleçteki amaç bu sınıftan iki nesneyi karşılaştırmak olduğu için, işlevi tanımlarken yapılması gereken ilk şey, parametre olarak gelen Object'in türünü kendi sınıfımızın türüne dönüştürmektir. Sağdaki nesneyi değiştirmek gibi bir niyetimiz de olmadığı için, tür dönüşümünde const belirtecini kullanmak da uygun olur:

    override bool opEquals(Object o) const
    {
        auto sağdaki = cast(const Saat)o;

        // ...
    }

Yukarıdaki tür dönüşümü işlemi ya sağdaki'nin türünü bu şekilde const Saat olarak belirler ya da dönüşüm uyumsuzsa null üretir.

Burada karar verilmesi gereken önemli bir konu, sağdaki nesnenin türünün bu nesnenin türü ile aynı olmadığında ne olacağıdır. Sağdaki nesnenin tür dönüşümü sonucunda null üretmesi, sağdaki nesnenin aslında bu türe dönüştürülemediği anlamına gelir.

Ben nesnelerin eşit kabul edilebilmeleri için bu dönüşümün başarılı olması gerektiğini varsayacağım. Bu yüzden eşitlik karşılaştırmalarında öncelikle sağdaki'nin null olmadığına bakacağım. Zaten null olduğu durumda sağdaki'nin üyelerine erişmek hatalıdır:

class Saat
{
    int saat;
    int dakika;
    int saniye;

    override bool opEquals(Object o) const
    {
        auto sağdaki = cast(const Saat)o;

        return (sağdaki &&
                (saat == sağdaki.saat) &&
                (dakika == sağdaki.dakika) &&
                (saniye == sağdaki.saniye));
    }

    // ...
}

İşlevin bu tanımı sayesinde, == işleci Saat nesnelerini artık değerlerine göre karşılaştırır:

    auto değişken0 = new Saat(6, 7, 8);
    auto değişken1 = new Saat(6, 7, 8);

    assert(değişken0 == değişken1); // artık eşitler
                                    // (çünkü değerleri aynı)

opEquals'ı tanımlarken, eğer varsa ve nesnelerin eşit kabul edilmeleri için gerekliyse, üst sınıfın üyelerini de unutmamak gerekir. Örneğin alt sınıf olan ÇalarSaat'in nesnelerini karşılaştırırken, Saat'ten kalıtımla edindiği parçaları da karşılaştırmak anlamlı olur:

class ÇalarSaat : Saat
{
    int alarmSaati;
    int alarmDakikası;

    override bool opEquals(Object o) const
    {
        auto sağdaki = cast(const ÇalarSaat)o;

        return (sağdaki &&
                (super == o) &&
                (alarmSaati == sağdaki.alarmSaati) &&
                (alarmDakikası == sağdaki.alarmDakikası));
    }

    // ...
}

Oradaki ifade super'in opEquals işlevini çağırır ve eşitlik kararında onun da sonucunu kullanmış olur.

opCmp

Sınıf nesnelerini sıralamak için kullanılır. <, <=, >, ve >= işleçlerinin tanımı için perde arkasında bu işlev kullanılır.

Bu işlevin dönüş değerini < işleci üzerinde düşünebilirsiniz: Soldaki nesne önce olduğunda eksi bir değer, sağdaki nesne önce olduğunda artı bir değer, ikisi eşit olduklarında sıfır döndürmelidir.

Uyarı: Bu işlevin opEquals ile tutarlı olması gerekir; sıfır döndürdüğü durumda opEquals da true döndürmelidir.

toString'in ve opEquals'un aksine, bu işlevin Object sınıfından kalıtımla edinilen bir davranışı yoktur. Tanımlanmadan kullanılırsa hata atılır:

    auto değişken0 = new Saat(6, 7, 8);
    auto değişken1 = new Saat(6, 7, 8);

    assert(değişken0 < değişken1);     // ← Program çöker
object.Exception: need opCmp for class deneme.Saat

Yukarıda opEquals için söylenenler bu işlev için de geçerlidir: Sağdaki nesnenin türünün bu nesnenin türüne eşit olmadığı durumda hangisinin daha önce sıralanması gerektiği konusuna bir şekilde karar vermek gerekir.

Bunun en kolayı bu kararı derleyiciye bırakmaktır, çünkü derleyici türler arasında zaten genel bir sıralama belirler. Türler aynı olmadıklarında bu sıralamadan yararlanmanın yolu, typeid'lerinin opCmp işlevinden yararlanmaktır:

class Saat
{
    int saat;
    int dakika;
    int saniye;

    override int opCmp(Object o) const
    {
        /* Türler aynı olmadıklarında türlerin genel
         * sıralamasından yararlanıyoruz. */
        if (typeid(this) != typeid(o)) {
            return typeid(this).opCmp(typeid(o));
        }

        auto sağdaki = cast(const Saat)o;

        if (saat != sağdaki.saat) {
            return saat - sağdaki.saat;

        } else if (dakika != sağdaki.dakika) {
            return dakika - sağdaki.dakika;

        } else {
            return saniye - sağdaki.saniye;
        }
    }

    // ...
}

Yukarıdaki tanım, nesneleri sıralama amacıyla karşılaştırırken öncelikle türlerinin uyumlu olup olmadıklarına bakıyor. Eğer uyumlu iseler saat bilgisini dikkate alıyor; saatler eşitlerse dakikalara, onlar da eşitlerse saniyelere bakıyor.

Ne yazık ki, bu işlevin bu gibi karşılaştırmalarda daha güzel veya daha etkin bir yazımı yoktur. Eğer daha uygun bulursanız, if-else-if zinciri yerine onun eşdeğeri olan üçlü işleci de kullanabilirsiniz:

    override int opCmp(Object o) const
    {
        if (typeid(this) != typeid(o)) {
            return typeid(this).opCmp(typeid(o));
        }

        auto sağdaki = cast(const Saat)o;

        return (saat != sağdaki.saat
                ? saat - sağdaki.saat
                : (dakika != sağdaki.dakika
                   ? dakika - sağdaki.dakika
                   : saniye - sağdaki.saniye));
    }

Bu işlevi bir alt sınıf için tanımlarken ve karşılaştırmada önemi varsa, üst sınıfını da unutmamak gerekir. Örneğin ÇalarSaat için:

    override int opCmp(Object o) const
    {
        auto sağdaki = cast(const ÇalarSaat)o;

        const int üstSonuç = super.opCmp(o);

        if (üstSonuç != 0) {
            return üstSonuç;

        } else if (alarmSaati != sağdaki.alarmSaati) {
            return alarmSaati - sağdaki.alarmSaati;

        } else {
            return alarmDakikası - sağdaki.alarmDakikası;
        }
    }

Üst sınıfın sıfırdan farklı bir değer döndürmesi durumunda, iki nesnenin sıraları ile ilgili yeterli bilgi edinilmiştir; ve o değer döndürülür. Yukarıdaki kodda, alt sınıfın üyelerine ancak üst sınıf parçaları eşit çıktığında bakılmaktadır.

Artık bu türün nesneleri sıralama karşılaştırmalarında kullanılabilir:

    auto çs0 = new ÇalarSaat(8, 0, 0, 6, 30);
    auto çs1 = new ÇalarSaat(8, 0, 0, 6, 31);

    assert(çs0 < çs1);

O kodda diğer bütün üyeleri eşit olduğu için, çs0 ve çs1'in nasıl sıralanacaklarını en son bakılan alarm dakikası belirler.

Bu işlev yalnızca kendi yazdığımız kodlarda kullanılmak için değildir. Programda kullandığımız kütüphaneler ve dil olanakları da bu işlevi çağırabilir. Örneğin bir dizi içindeki nesnelerin .sort ile sıralanmalarında, veya sınıfın bir eşleme tablosunda indeks türü olarak kullanılmasında da perde arkasında bu işlevden yararlanılır.

Yukarıdaki tanımlarımızı denemek için, rasgele saat değerleriyle kurulan ÇalarSaat nesnelerini içeren bir diziyi sıralayalım. Sınıfların yalnızca bu programda kullanılan işlevlerini gösteriyorum:

import std.string;
import std.random;
import std.stdio;

class Saat
{
    int saat;
    int dakika;
    int saniye;

    this(int saat, int dakika, int saniye)
    {
        this.saat = saat;
        this.dakika = dakika;
        this.saniye = saniye;
    }

    override string toString() const
    {
        return format("%02s:%02s:%02s", saat, dakika, saniye);
    }

    override int opCmp(Object o) const
    {
        auto sağdaki = cast(const Saat)o;

        if (saat != sağdaki.saat) {
            return saat - sağdaki.saat;

        } else if (dakika != sağdaki.dakika) {
            return dakika - sağdaki.dakika;

        } else {
            return saniye - sağdaki.saniye;
        }
    }
}

class ÇalarSaat : Saat
{
    int alarmSaati;
    int alarmDakikası;

    this(int saat, int dakika, int saniye,
         int alarmSaati, int alarmDakikası)
    {
        super(saat, dakika, saniye);
        this.alarmSaati = alarmSaati;
        this.alarmDakikası = alarmDakikası;
    }

    override string toString() const
    {
        return format("%s ♫%02s:%02s", super.toString(),
                      alarmSaati, alarmDakikası);
    }

    override int opCmp(Object o) const
    {
        auto sağdaki = cast(const ÇalarSaat)o;

        const int üstSonuç = super.opCmp(o);

        if (üstSonuç != 0) {
            return üstSonuç;

        } else if (alarmSaati != sağdaki.alarmSaati) {
            return alarmSaati - sağdaki.alarmSaati;

        } else {
            return alarmDakikası - sağdaki.alarmDakikası;
        }
    }
}

void main()
{
    ÇalarSaat[] saatler;

    foreach (i; 0 .. 10) {
        saatler ~= new ÇalarSaat(uniform(0, 24),
                                 uniform(0, 60),
                                 uniform(0, 60),
                                 uniform(0, 24),
                                 uniform(0, 60));
    }

    saatler.sort;

    foreach (saat; saatler) {
        writeln(saat);
    }
}

Yalnızca on nesne kullanan bu örnekte sıralamanın öncelikle nesnelerin saat değerleri tarafından belirlendiği görülüyor:

00:00:30 ♫06:25
06:27:38 ♫16:24
14:05:19 ♫13:05
17:48:09 ♫14:06
18:51:51 ♫23:31
20:43:11 ♫07:42
21:56:05 ♫07:47
22:52:08 ♫10:42
23:39:08 ♫09:44
23:55:04 ♫11:22

Çıktının yalnızca son iki nesnesinin sıralanmalarında dakika'ların değerlerine bakıldığı anlaşılıyor. Daha fazla nesne kullandığınızda veya kurucuya verilen rasgele değer aralıklarıyla oynadığınızda daha belirgin sonuçlar göreceksinizdir.

Dizgi türünden olan üyeler için opCmp

Dizgi üyeler için opCmp işlevini eksi, sıfır, veya artı döndürecek şekilde uzun uzun şöyle yazabilirsiniz:

class Öğrenci
{
    string isim;

    override int opCmp(Object o) const
    {
        auto sağdaki = cast(Öğrenci)o;

        if (isim < sağdaki.isim) {
            return -1;

        } else if (isim > sağdaki.isim) {
            return 1;

        } else {
            return 0;
        }
    }

    // ...
}

Onun yerine, std.string modülünde tanımlanmış olan ve aynı karşılaştırmayı büyük olasılıkla daha hızlı olarak gerçekleştiren cmp işlevini de kullanabilirsiniz:

import std.string;

class Öğrenci
{
    string isim;

    override int opCmp(Object o) const
    {
        auto sağdaki = cast(Öğrenci)o;

        return cmp(isim, sağdaki.isim);
    }

    // ...
}
toHash

Bu işlev, sınıfın eşleme tablolarında indeks türü olarak kullanılabilmesini sağlar. Eşleme tablosunun eleman türü olarak kullanıldığı durumda bir etkisi yoktur.

Uyarı: Yalnızca bu işlevi tanımlamak yetmez. Bu işlevin eşleme tablolarında doğru olarak kullanılabilmesi için opEquals ve opCmp işlevlerinin de birbirleriyle tutarlı olarak tanımlanmış olmaları gerekir.

Eşleme tablosu indeks değerleri

Eşleme tabloları eleman erişimini çok hızlı şekilde gerçekleştiren veri yapılarıdır. Üstelik bunu, tabloda ne kadar eleman bulunduğundan bağımsız olarak yapabilirler. (Not: Her şeyin olduğu gibi bu hızın da bir bedeli vardır: elemanları sırasız olarak tutmak zorundadırlar, ve kesinlikle gereken miktardan daha fazla bellek kullanıyor olabilirler.)

Eşleme tablolarının bu hızı, indeks olarak kullanılan türü önce hash denen bir tamsayı değere çevirmelerinden kaynaklanır. Bu tamsayıyı kendilerine ait bir dizinin indeksi olarak kullanırlar.

Bu yöntemin hızdan başka bir yararı, tamsayıya dönüştürülebilen her türün eşleme tablosu indeks türü olarak kullanılabilmesidir.

toHash, sınıf nesnelerinin bu amaç için indeks değerleri döndürmelerini sağlar.

Bu sayede, pek mantıklı olmasa da, Saat türünü bile indeks olarak kullanabiliriz:

  string[Saat] zamanİsimleri;

  zamanİsimleri[new Saat(12, 0, 0)] = "öğleni gösteren saat";

Object'ten kalıtım yoluyla edinilen toHash işlevi, farklı nesneler için farklı indeks değerleri üretecek şekilde tanımlanmıştır. Bu, opEquals'un farklı nesnelerin eşit olmadıklarını kabul etmesine benzer.

Yukarıdaki kod Saat sınıfı için özel bir toHash işlevi tanımlanmamış olsa bile derlenir; ama istediğimiz gibi çalışmaz. Yukarıdaki tabloya eklenmiş olan Saat nesnesi ile aynı değere sahip olan, ama ondan farklı bir Saat nesnesi ile erişmek istesek; doğal olarak tablodaki "öğleni gösteren saat" değerini bulmayı bekleriz:

    if (new Saat(12, 0, 0) in zamanİsimleri) {
        writeln("var");

    } else {
        writeln("yok");
    }

Ne yazık ki, oradaki in işleci false döndürür; yani bu nesnenin tabloda bulunmadığını belirtir:

yok

Bunun nedeni, yerleştirilirken kullanılan nesne ile erişirken kullanılan nesnenin new ile ayrı ayrı oluşturulmuş olmalarıdır; yani ikisi farklı nesnelerdir.

Dolayısıyla; Object'ten kalıtımla edinilen toHash, eşleme tablolarında indeks değeri olarak kullanılmaya çoğu durumda elverişli değildir. toHash'i, bir tamsayı indeks döndürecek şekilde bizim yazmamız gerekir.

toHash için seçilecek üyeler

İndeks değeri, nesnenin üyeleri kullanılarak hesaplanır. Ancak, her üye bu indeks hesabına uygun değildir.

Bunun için seçilecek üyeler, nesneyi diğer nesnelerden ayırt etmeye yarayan üyeler olmalıdır. Örneğin Öğrenci gibi bir sınıfın isim ve soyad üyelerinin ikisi birden nesneleri ayırt etmek için kullanılabilir; çünkü bu iki üyenin her nesnede farklı olduğunu düşünebiliriz. (İsim benzerliklerini gözardı ediyorum.)

Öte yandan, Öğrenci sınıfının notlar dizisi uygun değildir; çünkü hem birden fazla nesnede aynı not değerleri bulunabilir; hem de aynı öğrencinin notları zamanla değişebilir.

Tamsayı üyeler için indeks değerleri

Saat nesnelerinin farklı kabul edilebilmeleri için bütün üyelerinin değerlerinin önemli olduğunu düşünebiliriz. Bu yüzden, indeks değeri olarak o üç üyenin toplanması ile elde edilen tamsayı kullanılabilir. Bu sayede, herhangi bir üyesi değişik olan iki nesnenin indeks değerlerinin farklı olacağı garanti edilmiş olur:

class Saat
{
    int saat;
    int dakika;
    int saniye;

    override hash_t toHash() const
    {
        return saat + dakika + saniye;
    }

    // ...
}

Bu işlevin dönüş değeri olarak yazılan hash_t, işaretsiz bir türü temsil eder. Ortama bağlı olarak uint, ulong, veya daha başka bir tür olabilir. Bu işlevi tanımlarken dönüş türünü hash_t olarak yazmanızı öneririm. Bizim için asıl önemli olan, üyeleri kullanarak bir tamsayı değer üretmektir.

Saat eşleme tablolarında indeks türü olarak kullanıldığında, artık bizim tanımladığımız bu toHash kullanılır. Yukarıdaki kodda new ile farklı olarak kurulmuş olsalar bile; ikisinin saat, dakika, ve saniye değerleri aynı olduğu için eşleme tablosunda aynı indeks değerini üretirler.

Programın çıktısı artık beklenen sonucu verir:

var

Önceki işlevlerde olduğu gibi, üst sınıfı unutmamamız gerekebilir. Örneğin ÇalarSaat'in toHash işlevi, Saat'inkini şöyle kullanabilir:

class ÇalarSaat : Saat
{
    int alarmSaati;
    int alarmDakikası;

    override hash_t toHash() const
    {
        return super.toHash() + alarmSaati + alarmDakikası;
    }

    // ...
}
Tamsayı olmayan üyeler için indeks değerleri

Tamsayı üyeleri kullanarak indeks değeri üretmek onları toplamak kadar basittir.

D dili; kesirli sayılar, dizgiler, ve yapı türleri için çoğu duruma uygun olan indeks değeri algoritmaları kullanır. Bu algoritmalardan biz de yararlanabiliriz.

Kulağa karmaşık geldiği halde aslında çok kısaca yapmamız gereken; önce typeid'yi üye ile, sonra da typeid'nin döndürdüğü nesnenin getHash üye işlevini üyenin adresi ile çağırmaktır. Hepsinin dönüş değeri, o üyeye uygun bir indeks değeridir. Bu; kesirli sayılar, dizgiler ve yapılar için hep aynı şekilde yazılır.

Öğrencinin ismini bir string üyesinde tutan ve eşleme tabloları için indeks değeri olarak bundan yararlanmak isteyen bir sınıfın toHash işlevi şöyle yazılabilir:

class Öğrenci
{
    string isim;

    override hash_t toHash() const
    {
        return typeid(isim).getHash(&isim);
    }

    // ...
}

Aynı yazımı her tür için kullanabilirsiniz. Bir tamsayı değer ürettiği için, birden fazla üye için çağırarak değerlerini toplayabilirsiniz:

        return (typeid(isim).getHash(&isim) +
                typeid(soyAd).getHash(&soyAd));
Yapılar için toHash

Yapılar değer türleri olarak kabul edildikleri için, onların indeks değerleri zaten otomatik olarak ve etkin bir algoritmayla hesaplanır. O algoritma, nesnenin bütün üyelerini dikkate alır.

Eğer herhangi bir nedenle, örneğin bir öğrenci yapısının not bilgisini dışarıda bırakacak şekilde kendiniz yazmak isterseniz; toHash'i yapılar için de tanımlayabilirsiniz.

Problemler
  1. Elimizde renkli noktaları ifade eden bir sınıf olsun:
  2. enum Renk { mavi, yeşil, kırmızı };
    
    class Nokta
    {
        int x;
        int y;
        Renk renk;
    
        this(int x, int y, Renk renk)
        {
            this.x = x;
            this.y = y;
            this.renk = renk;
        }
    }
    

    Bu sınıfın opEquals işlevini rengi gözardı edecek şekilde yazın. Şu iki nokta, renkleri farklı olduğu halde eşit çıksınlar. Yani assert denetimi doğru çıksın:

        // Renkleri farklı
        auto maviNokta = new Nokta(1, 2, Renk.mavi);
        auto yeşilNokta = new Nokta(1, 2, Renk.yeşil);
    
        // Yine de eşitler
        assert(maviNokta == yeşilNokta);
    
  3. Aynı sınıf için opCmp işlevini; öncelikle x'e, sonra y'ye bakacak şekilde yazın. Aşağıdaki assert denetimleri doğru çıksın:
  4.     auto kırmızıNokta1 = new Nokta(-1, 10, Renk.kırmızı);
        auto kırmızıNokta2 = new Nokta(-2, 10, Renk.kırmızı);
        auto kırmızıNokta3 = new Nokta(-2,  7, Renk.kırmızı);
    
        assert(kırmızıNokta1 < maviNokta);
        assert(kırmızıNokta3 < kırmızıNokta2);
    
  5. Üç noktayı bir araya getiren bir sınıf daha olsun:
  6. class ÜçgenBölge
    {
        Nokta[3] noktalar;
    
        this(Nokta bir, Nokta iki, Nokta üç)
        {
            noktalar = [ bir, iki, üç ];
        }
    }
    

    O sınıf için toHash işlevini bütün noktalarını kullanacak şekilde yazın. Yine aşağıdaki assert'ler doğru çıksın:

        /*
          Farklı ama aynı değerli noktalarla kuruluyorlar.
    
          Hatırlatma: maviNokta ve yeşilNokta değer olarak eşit
                      kabul ediliyorlardı.
        */
        auto bölge1 =
            new ÜçgenBölge(maviNokta, yeşilNokta, kırmızıNokta1);
        auto bölge2 =
            new ÜçgenBölge(yeşilNokta, maviNokta, kırmızıNokta1);
    
        // Yine de eşitler
        assert(bölge1 == bölge2);
    
        // Bir eşleme tablosu
        double[ÜçgenBölge] bölgeler;
    
        // bölge1 ile indeksleniyor
        bölgeler[bölge1] = 1.25;
    
        // bölge2 ile de aynı veriye erişiliyor
        assert(bölge2 in bölgeler);
        assert(bölgeler[bölge2] == 1.25);
    

    Bu sınıf için opEquals ve opCmp işlevlerini de yazmanız gerektiğini unutmayın.

... çözümler