Thread overview
Kendi türlerimiz için foreach desteği
Jun 01, 2012
Salih Dinçer
November 21, 2010

"Yapı ve Sınıflarda foreach" dersinin son problemi, Okul sınıfının öğrenciler ve öğretmenler için farklı foreach desteği vermesiydi:

http://ddili.org/ders/d/foreach_opapply.html

D.ershane'deki hızımı iyice kaybetmiş olarak o derse eklemek yerine buraya bir çözüm yazıyorum. :)

Tabii program asıl amacından ayrıldı ve üye nitelik eklemeye yardım eden bir de şablon edindi. :) üyeNitelik şablonu, isminin sonuna alt çizgi eklediği 'private' bir üye tanımlıyor ve bu üyeye 'const' erişim sağlayan bir de nitelik işlevi oluşturuyor. Ama asıl konumuz o değil. :)

Okul sınıfının Öğrenci ve Öğretmen'ler için farklı foreach desteği, farklı iki opApply yüklemesiyle sağlanıyor.

Açıklamalar kodun içinde (ve yukarıdaki derste):

import std.stdio;
import std.string;

/*
* Sınıf veya yapı üyesi tanımlamaya yardımcı olan bir şablon
* katması. Belirtilen isimde 'private' bir üye, ve o üyeye 'const' erişim
* sağlayan bir nitelik işlevi tanımlar.
*
* Ürettiği örnek kod:
*
*     private dstring isim_;
*     public @property dstring isim() const
*     {
*         return isim_;
*     }
*/
template üyeNitelik(Tür, dstring değişken)
{
   /*
    * Şu anda bir 'şablon katması' (template mixin) tanımlıyoruz. Bu şablon,
    * aşağıdaki sınıf tanımlarında 'üyeNitelik!' yazımıyla kullanılacak.
    *
    * İlginç olarak, bu katma, kendisi bir 'dizgi katması' (string mixin)
    * kullanıyor:
    */
   mixin (
       "private " ~ Tür.stringof ~ " " ~ değişken ~ "_;" ~
       "public @property " ~ Tür.stringof ~ " " ~ değişken ~ "() const" ~
       "{ " ~
       "    return " ~ değişken ~ "_;" ~
       "}");

   /*
    * Daha fazla bilgi için:
    *
    *   http://ddili.org/ders/d/katmalar.html
    */
}

class Öğrenci
{
   mixin üyeNitelik!(dstring, "isim");
   mixin üyeNitelik!(uint, "numara");

   this(const dstring isim, uint numara)
   {
       this.isim_ = isim;
       this.numara_ = numara;
   }

   override string toString() const
   {
       return format("%s %s", numara, isim);
   }
}

class Öğretmen
{
   mixin üyeNitelik!(dstring, "isim");
   mixin üyeNitelik!(dstring, "ders");

   this(const dstring isim, const dstring ders)
   {
       this.isim_ = isim;
       this.ders_ = ders;
   }

   override string toString() const
   {
       return format("%s dersine %s öğretmen", ders, isim);
   }
}

class Okul
{
private:

   Öğrenci[] öğrenciler;
   Öğretmen[] öğretmenler;

public:

   this(const Öğrenci[] öğrenciler, const Öğretmen[] öğretmenler)
   {
       this.öğrenciler = öğrenciler.dup;
       this.öğretmenler = öğretmenler.dup;
   }

   /*
    * Öğrenci türü için foreach desteği
    *
    * Kullanıcının yazdığı foreach'in kapsamı, opApply işlecine bir delegate
    * olarak gönderilir. foreach'e yazılan parametrenin türü Öğrenci
    * olduğunda, burada tanımlanan delegate'in türüne uyar ve bu opApply
    * işleci çağrılır.
    *
    * Daha fazla bilgi için:
    *
    *   http://ddili.org/ders/d/foreach_opapply.html
    */
   int opApply(int delegate(ref Öğrenci) işlemler)
   {
       int sonuç;

       /*
        * Yapmamız gereken, öğrencileri teker teker kullanıcının delegate'ine
        * (ve dolaylı olarak kullanıcının foreach döngüsüne) göndermektir.
        */
       foreach (öğrenci; öğrenciler) {
           sonuç = işlemler(öğrenci);

           /*
            * Kullanıcının foreach döngüsü 'break' ile sonlanmış olduğu, ve
            * dolayısıyla buradaki döngünün de kırılması gerektiği, 'sonuç'
            * değişkeninin sıfırdan farklı bir değer almasından anlaşılır.
            *
            * Öyle olduğunda bu döngünün de 'break' ile kırılması gerekir.
            */
           if (sonuç) {
               break;
           }
       }

       /*
        * Döngünün nasıl sonlanmış olduğunu bildirmek için 'sonuç'un değeri
        * döndürülür.
        */
       return sonuç;
   }

   /**
    * Yukarıdakinin benzeri olarak Öğretmen türü için foreach desteği
    */
   int opApply(int delegate(ref Öğretmen) işlemler)
   {
       int sonuç;

       foreach (öğretmen; öğretmenler) {
           sonuç = işlemler(öğretmen);

           if (sonuç) {
               break;
           }
       }

       return sonuç;
   }
}

void girintiliYazdır(T)(T nesne)
{
   writeln("  ", nesne);
}

void main()
{
   auto okul = new Okul([ new Öğrenci("Can", 1),
                          new Öğrenci("Canan", 10),
                          new Öğrenci("Cem", 42),
                          new Öğrenci("Cemile", 100) ],

                        [ new Öğretmen("Nazmiye", "Matematik"),
                          new Öğretmen("Makbule", "Türkçe") ]);

   writeln("Öğrenci döngüsü");
   foreach (Öğrenci öğrenci; okul) {
       girintiliYazdır(öğrenci);
   }

   writeln("Öğretmen döngüsü");
   foreach (Öğretmen öğretmen; okul) {
       girintiliYazdır(öğretmen);
   }
}

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

June 01, 2012

Bu da çok iyimiş!

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

June 01, 2012

Bence de çok iyi. :)

Hatırlatmak için: Bundan daha kolay da olabiliyor. Çoğu durumda InputRange aralığı işlevleri olan front(), popFront(), ve empty()'yi tanımlamak yeterli.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]