Thread overview
InputRange örneği
Dec 21, 2010
Mengu
December 21, 2010

ustad, ne diyosun? :)

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

December 21, 2010

Daha önce bir OutputRange örneği yazmıştım:

http://ddili.org/forum/thread/421

O iş 'put' işlevini tanımlamak kadar kolaydı. Aralık denemelerime devam ediyorum... :)

Bir türün InputRange olarak kabul edilebilmesi için empty, popFront, ve front işlevlerini tanımlaması gerekir.

empty: aralık boş kabul edildiğinde 'true', değilse 'false' döndürmelidir

front: aralığın başındaki elemanı döndürmelidir

popFront: aralığı baş tarafından daraltmalıdır

Ayrıca, eğer empty 'true' döndürüyorsa, diğer iki işlevin çağrılması yasal değildir.

Bunlarla ilgili bilgi şurada "foreach desteğini aralık işlevleri ile sağlamak" başlığı altında da vardı:

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

O üç işleve sahip olması türün InputRange olması için yeterlidir. (O derste de görüldüğü gibi ve bence D'nin biraz ilginç bir olanağı sonucunda da türün foreach ile çalışabilmesini de sağlıyor.)

InputRange örneği olarak şöyle bir sınıf düşündüm: Elimizde bulunan bazı değerleri komşu değerlerin ortalamalarını alarak azaltmak isteyelim. Örneğin 1, 2, 3, 4 değerlerini ikişer ikişer ortalayarak 2 değere indirebiliriz: (1+2)/2 ve (3+4)/2. Yani 1.5 ve 3.5. (Bunu, bir büyüklüğün belirli aralıklarla ölçülmüş değerlerini yuvarlamak için kullanabiliriz. Örneğin bir grafiği daha küçük göstermek istemişizdir ve 1000 noktayı onar onar ortalayarak 100 nokta olarak göstermek istemişizdir.)

import std.stdio;
import std.algorithm;

class DeğerOrtalayıcı
{
   const(double)[] değerler;

   /* Kaç değerin ortalamasının yeni bir değer oluşturacağı; örneğin 3
    * olduğunda noktaları üçer üçer ortalayacağız */
   int ortalamaMiktarı;

   this(const(double)[] değerler, int ortalamaMiktarı)
   {
       this.değerler = değerler;
       this.ortalamaMiktarı = ortalamaMiktarı;
   }

   /* Bu işlev aralık boş kabul edildiğinde 'true' döndürmelidir */
   @property bool empty() const
   {
       /* Kod basit olsun diye sonda bulunan ve ortalamaMiktarı'ndan daha az
        * sayıda kaldıkları için kullanılamayacak olan değerleri gözardı
        * edelim. Yani, sonda kalanların sayısı ortalamaMiktarı'ndan az ise
        * bizim için aralık boş demektir. */
       return değerler.length < ortalamaMiktarı;
   }

   /* Bu işlev aralığın başındaki elemanı döndürmelidir
    *
    * Burada, asıl değerlerin başındaki birden fazla değerin ortalamasını
    * alacağız. Zaten bu sınıfın bütün amacı da o. */
   @property double front() const
   {
       double toplam = 0.0;

       foreach (i; 0 .. ortalamaMiktarı) {
           toplam += değerler[i];
       }

       return toplam / ortalamaMiktarı;
   }

   /* Bu işlev baştaki elemanı aralıktan çıkartmalıdır
    *
    * Bizim için "baştaki eleman" kavramı ortalamaMiktarı kadar değerden
    * oluştuğu için, asıl değerlerin ortalamaMiktarı kadarını
    * çıkartmalıyız */
   void popFront()
   {
       değerler = değerler[ortalamaMiktarı .. $];
   }
}

/* Deneme amacıyla bazı değerler */
double[] örnekDeğerler(int adet)
{
   double[] değerler;

   foreach (i; 0 .. adet) {
       değerler ~= i * 1.1;
   }

   return değerler;
}

void main()
{
   double[] değerler = örnekDeğerler(11);
   int ortalamaMiktarı = 2;

   auto ortalanmışAralık = new DeğerOrtalayıcı(değerler, ortalamaMiktarı);

   writeln("Asıl değerler: ", değerler);

   write("Ortalanmış değerler: ");
   foreach (değer; ortalanmışAralık) {
       write(değer, ' ');
   }

   writeln();
}

Çıktısı:

'Asıl değerler: [0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 11]
Ortalanmış değerler: 0.55 2.75 4.95 7.15 9.35 '

Artık bu sınıfı Phobos'un InputRange kullanan algoritmalarıyla da kullanabiliriz. Örneğin 'findAmong', bir aralıkta bulunan elemanları başka bir aralıkta arar ve bulduğu ilk elemandan gerisini yeni bir aralık olarak döndürür:

   double[] değerler = örnekDeğerler(11);
   int ortalamaMiktarı = 2;

   auto ortalanmışAralık = new DeğerOrtalayıcı(değerler, ortalamaMiktarı);
   DeğerOrtalayıcı sonuçAralık = findAmong(ortalanmışAralık,
                                           [ 0, 1, 2.75, 7, 10 ]);

   writeln("Asıl değerler: ", değerler);

   write("Bulunan elemandan başlayan sonuç aralık: ");
   foreach (değer; sonuçAralık) {
       write(değer, ' ');
   }
   writeln();

Bir önceki çıktıda 2.75 değerini gördüğüm için aranacak değerlerin arasına bir tane de ondan yerleştirdim: [ 0, 1, 2.75, 7, 10 ] :). findAmong 2.75'i ortalanmış aralıkta bulur ve gerisini sonuç olarak döndürür:

'Bulunan elemandan başlayan sonuç aralık: 2.75 4.95 7.15 9.35 '

(Not: Kesirli sayıları eşitlik karşılaştırmalarına sokmak genelde yanlıştır. Ben, kesirli kısmı ikinin katlarına bölünmüş bir değer olduğu için 2.75'i seçtim: 2 + 1/2 + 1/4, kesirli bir değer olduğu halde eşitlik karşılaştırmalarından sağlam çıkar. :))

Tamam; amacımıza ulaştık ve hem foreach ile hem de InputRange gerektiren algoritmalarla çalışabilen bir tür yazdık.

Ali

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

December 21, 2010

Mengü, sorunu biraz daha açar mısın... :-p :D :D

D'nin aralıklarını (ranges) tanımak için deneyler yapıyorum. Bir yandan da başımdan geçenleri yazıyorum. Pek bir anlaşılmaz olduğunu biliyorum ama yine de örnek olarak bir kenarda bulunsunlar.

Devamı gelecek... :)

Ali

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