Thread overview
delegate üzerinde foreach
Oct 05, 2011
zafer
Oct 06, 2011
zafer
October 05, 2011

Bunu bilmiyordum. Ya sonradan eklenmiş, ya da şurada 'Foreach over Delegates' başlığı altındaki iki satırlık (!) bilgiyi gözden kaçırmış olmalıyım: ;)

http://www.d-programming-language.org/statement.html#ForeachStatement

foreach'in temel olanaklarla nasıl işlediğini biliyoruz:

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

foreach'i kendi türlerimiz için de tanımlayabiliyoruz:

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

Orada anlatıldığına göre,

  • basit durumlarda aralık işlevleri kullanmak akıllıca

  • gerektiğinde ise türün opApply() işlevini tanımlıyoruz

opApply() yüklenebiliyor; foreach döngüsünün değişken tanımlarına uygun olan opApply() seçiliyor. Böylece geçende konuştuğumuz gibi ve yukarıdaki bölümün problemlerinin 2 numaralısında da olduğu gibi belirli bir topluluk üzerinde farklı biçimlerde ilerleyebiliyoruz.

Orada Öğrenci ve Öğretmen türlerinin farklı olmalarından yararlanılıyor. Oysa foreach değişkenlerinin aynı türlerde oldukları için opApply()'ın yüklenemeyeceği durumlar olabilir. Sanırım bu yüzden opApply()'ı delegate'ler üzerinde de kullanma olanağı sağlamışlar. delegate'leri "kapamalar" adı altında şurada anlatmıştım (ama bu isimden memnun değilim çünkü delegate'lerin ancak belirli bir kullanımı fonksiyonel dillerdeki kapamaların (closure) eşdeğeri oluyor):

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

foreach ile delegate kullanmanın gerekebileceği şöyle bir örnek düşünebiliriz. (İhtiyaç duymayınca örnek bulmak zor. :)) Düzlemdeki noktaların x,y koordinatlarından oluşan Noktalar diye bir tür olsun. Örneğin bir poligonun noktalarını barındırıyor olabilir.

foreach'i bu tür için "x değerleri üzerinde" veya "y değerleri üzerinde" ilerleyecek biçimde ancak delegate ile kullanabiliyoruz çünkü aralık işlevleri kullanıldığında front() belirli türden bir değer döndürür ve o tür de doğal olarak Nokta'dır.

opApply() yüklendiğinde ise hangisinin kullanılacağı foreach'in değişkenlerinin türüne bağlı olduğundan ve ne yazık ki x ve y ikisi de int olduklarından opApply() yüklemesi de kullanamıyoruz.

O yüzden burada çözüm, Noktalar türüne iki farklı üye işlev eklemek(miş :)). Bu üye işlevler opApply() ile aynı kimliğe sahip delegate'ler döndürmeliler(miş). Kulağa çok karmaşık geliyor, çünkü opApply() zaten delegate aldığı için bu üye işlevlerin "parametre olarak delegate alan delegate" döndürmeleri gerekiyor! :)

Ne kadar zor gibi gelse de, aslında dikkatle bakınca yine de bir opApply() yüklemesi gibi görünüyor ama bu yüklemeleri sanki farklı isimde üye işlevler içine gizliyoruz...

Bu iki üye işlevin döndürdüğü delegate'lerden birisi x değerlerini, diğeri de y değerlerini kullanacak.

import std.stdio;

struct Nokta
{
   int x;
   int y;
}

struct Noktalar
{
   Nokta[] noktalar;

   this(Nokta[] noktalar)
   {
       this.noktalar = noktalar;
   }

   /*
    * Burada dönüş türü olarak auto yazmak çok uygun. Çünkü dönüş türü aslında
    * şu kadar karmaşık:
    *
    *   int delegate(int delegate(ref int) işlemler)
    *
    * (Tabii alias kullanarak yazımını biraz olsun kolaylaştırabilirdik de.)
    */
   @property auto x_değerleri()
   {
       /* Burada 'işlemler', foreach bloğunu temsil ediyor */
       return delegate int(int delegate(ref int değer) işlemler) {
           int sonuç;
           foreach (nokta; noktalar) {
               sonuç = işlemler(nokta.x);  // <-- x değerleri
               if (sonuç) {
                   break;  // foreach bloğu içinde 'break'e rastlamışız demek
               }
           }
           return sonuç;
       };
   }

   /*
    * Bu işlev yukarıdakinin aynısı. Tek farkı, nokta.x yerine nokta.y yazılmış
    * olması.
    */
   @property auto y_değerleri()
   {
       return delegate int(int delegate(ref int değer) işlemler) {
           int sonuç;
           foreach (nokta; noktalar) {
               sonuç = işlemler(nokta.y);  // <-- y değerleri
               if (sonuç) {
                   break;
               }
           }
           return sonuç;
       };
   }

   /*
    * Karşılaştırmak amacıyla, Nokta değerlerini döndüren bir opApply()'ı da
    * şöyle yazabilirdik. Yukarıdakilerin buna ne kadar benzediğine dikkat
    * edin. Yukarıdaki fark, sanki opApply()'ın bir üye işlevden döndürülüyor
    * olması gibi düşünülebilir.
    */
   int opApply(int delegate(ref Nokta nokta) işlemler) {
       int sonuç;
       foreach (nokta; noktalar) {
           sonuç = işlemler(nokta);  // <-- noktalar
           if (sonuç) {
               break;
           }
       }
       return sonuç;
   }
}

void main()
{
   auto noktalar = Noktalar([ Nokta(1,2),
                              Nokta(3,4),
                              Nokta(5,6) ]);

   writeln("x değerleri:");
   foreach (değer; noktalar.x_değerleri) {
       writeln(değer);
   }

   writeln("y değerleri:");
   foreach (değer; noktalar.y_değerleri) {
       writeln(değer);
   }

   writeln("noktalar:");
   foreach (nokta; noktalar) {
       writeln(nokta);
   }
}

Çıktısı:

'x değerleri:
1
3
5
y değerleri:
2
4
6
noktalar:
Nokta(1, 2)
Nokta(3, 4)
Nokta(5, 6)
'

Üç işlev neredeyse aynı; tek farkları 'işlemler' delegate'ini nasıl çağırdıkları. Burada kodu biraz düzenleyebiliriz. Şimdi kod tekrarı azalıyor ama belki de kodun anlaşılması güçleşiyor:

struct Noktalar
{
   Nokta[] noktalar;

   this(Nokta[] noktalar)
   {
       this.noktalar = noktalar;
   }

   int noktaDöngüsü(T, İ)(İ işlemler, T delegate(Nokta nokta) erişici)
   {
       int sonuç;
       foreach (nokta; noktalar) {
           T değer = erişici(nokta);
           sonuç = işlemler(değer);
           if (sonuç) {
               break;
           }
       }
       return sonuç;
   }

   @property auto x_değerleri()
   {
       return delegate int(int delegate(ref int değer) işlemler) {
           return noktaDöngüsü!int(işlemler, (Nokta nokta){ return nokta.x; });
       };
   }

   @property auto y_değerleri()
   {
       return delegate int(int delegate(ref int değer) işlemler) {
           return noktaDöngüsü!int(işlemler, (Nokta nokta){ return nokta.y; });
       };
   }

   int opApply(int delegate(ref Nokta nokta) işlemler) {
       return noktaDöngüsü!Nokta(işlemler, (Nokta nokta){ return nokta; });
   }
}

Başım ağrıyor! :-p (Şaka!)

Ali

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

October 05, 2011

Eline sağlık güzel bir yazı olmuş ama aralıklar ve delegate konusunu bilmediğim için sadece okuyup geçtim. Aklıma takılanlar ise şunlar;

** Bu delegatenin C# dilindeki delegate ile bir benzerliği var mı?
** Delegateyi neden kapama olarak Türkçe'ye çevirdik?

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

October 06, 2011

Alıntı (zafer):

>

aralıklar ve delegate konusunu bilmediğim için sadece okuyup geçtim

Ben o konuları bildiğim halde bu opApply() bana çok karmaşık geldi. :) Kullanmak gerektiğinde hatırlamanın mümkün olmadığı bir konu. Tekrar yazmam gerekse kesinlikle örnek kodlar bulmam gerekir. :)

Alıntı:

>

Bu delegatenin C# dilindeki delegate ile bir benzerliği var mı?

Hiç C# bilmiyorum ama hızlıca bakınca ya aynı olanak ya da çok benzer olduklarını görüyorum. D'nin C#'tan olanaklar aldığını da biliyoruz; belki bu onlardan birisidir.

  • C ve C++'ta işlev göstergesi (function pointer) var

  • D'de işlev göstergesi karşılığı olarak 'function'

  • 'function', isimsiz işlevleri (lambda functions) de kapsıyor. (Bu aslında yeni C++11'e de eklendi.)

  • D'deki 'delegate' iki parçadan oluşuyor: bir 'function' ve bir de o 'function'ın başlatıldığı kapsam

C#'ınki de galiba aynısı.

Alıntı:

>

Delegateyi neden kapama olarak Türkçe'ye çevirdik?

Fonksiyonel dillerde fazla deneyimim yok ama 'delegate', fonksiyonel dillerdeki "closure" denen olanağı da destekliyor. Türkçe karşılık ararken bu yüzden onun karşılığı olarak "kapama"yı seçtim.

Ama sonradan forumlardaki tartışmalardan anladıklarıma göre, "closure" 'delegate'lerin desteklediği olanaklardan yalnızca birisiymiş. Başka bir deyişle, 'delegate'in yalnızca bazı kullanımları "closure"lara eşdeğer oluyormuş. Bu yüzden her zaman için "closure" demek doğru değilmiş. (Zaten o yüzden C#'çılar ve D'ciler de closure yerine delegate demiş olmalılar.)

C# dünyasında delegate'in Türkçe karşılığı var mı? Belki biz de onu kullanırız.

Ali

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

October 06, 2011

İşlerin bir anda yoğunlaşmasından dolayı ve birazda hastalığı bahane ederek biraz tembellik yapıyorum. Dolayısıyla hala yapılar bölümünü bitiremedim ve delegate konusunuda okuma fırsatım olmadı tabi. :scared:

C# dilindeki sanırım en basit tanımı şöyle;
Alıntı:

>

Temsilci (delegate), program içerisinde bir veya daha fazla metodu gösteren(işaret eden), referans türünden bir nesnedir.

C# dilinde delegate yerine temsilci kelimesi seçilmiş genelde böyle kullanılıyor. Aslında temsilci bence güzel ama yukarıda yazdığım gibi D dilindeki delegate kavramını henüz hiç tanımadığım için net olamıyorum. Yinede temsilci kapamadan daha iyi gibi duruyor :-D

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