June 25, 2010

Evet, düşündüm ve benim yanıtımın yanlış olduğuna karar verdim. :)

ÜçgenBölge sınıfının toHash işlevi, noktaların bütün üyelerini göze alamaz. Ama yanlışlık aslında ÜçgenBölge'de değil. Yanlışlık, Nokta'nın toHash işlevinin bulunmamasından kaynaklanıyor. Çünkü eşitlik ve sıra karşılaştırmalarını tanımladığı halde onlarla uyumlu bir toHash işlevi tanımlamamış.

Bunu görmek için yukarıdaki kodlardan oluşan aşağıdaki programı yazdım. 'hatasız' isimli bir version bloğu var; program ancak onu etkinleştirince doğru çalışıyor:

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

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;
   }

   override string toString() const
   {
       return format("(%s,%s,%s)", x, y, to!string(renk));
   }

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

       return (x == sağdaki.x) && (y == sağdaki.y);
   }

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

       return (x != sağdaki.x
               ? x - sağdaki.x
               : y - sağdaki.y);
   }

   version (hatasız)
   {
   override const hash_t toHash()
   {
       return x + y;
   }
   } // version hatasız
}

class ÜçgenBölge
{
   Nokta[3] noktalar;

   this(Nokta bir, Nokta iki, Nokta üç)
   {
       noktalar = [ bir, iki, üç ];
   }

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

   override const bool opEquals(Object o)
   {
       auto sağdaki = cast(const ÜçgenBölge)o;
       return noktalar == sağdaki.noktalar;
   }

   override const int opCmp(Object o)
   {
       auto sağdaki = cast(const ÜçgenBölge)o;

       foreach (i, nokta; noktalar) {
           const int karşılaştırma =
               nokta.opCmp(sağdaki.noktalar[i]);

           if (karşılaştırma != 0) {
               return karşılaştırma;
           }
       }

       return 0;
   }

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

Nokta merkezdeRenkliNokta()
{
   return new Nokta(0, 0, cast(Renk)uniform(0, Renk.max + 1));
}

ÜçgenBölge merkezdeÜçgenBölge()
{
   return new ÜçgenBölge(merkezdeRenkliNokta(),
                         merkezdeRenkliNokta(),
                         merkezdeRenkliNokta());
}

void main()
{
   int[ÜçgenBölge] tablo;

   /*
    * Tabloya aynı x ve y değerlerine sahip ama rastgele renkli noktalar
    * yerleştiriyoruz
    */
   foreach (sayaç; 0 .. 10) {
       auto bölge = merkezdeÜçgenBölge();
       tablo[bölge] = sayaç;
   }

   writeln("Bütün tablo:");
   foreach (indeks, değer; tablo) {
       writeln(indeks, ": ", değer);
   }

   /*
    * Rastgele renkli başka noktalar da hep tabloda çıkmalılar
    */
   foreach (sayaç; 0 .. 10) {
       assert(merkezdeÜçgenBölge() in tablo);
   }
}

O programı normal olarak derleyip çalıştırırsak çıktısı şöyle oluyor:

Bütün tablo:
[(0,0,mavi),(0,0,yeşil),(0,0,kırmızı)]: 0
[(0,0,yeşil),(0,0,yeşil),(0,0,mavi)]: 1
[(0,0,mavi),(0,0,yeşil),(0,0,kırmızı)]: 2
[(0,0,yeşil),(0,0,kırmızı),(0,0,yeşil)]: 3
[(0,0,yeşil),(0,0,kırmızı),(0,0,kırmızı)]: 4
[(0,0,mavi),(0,0,kırmızı),(0,0,mavi)]: 5
[(0,0,kırmızı),(0,0,yeşil),(0,0,mavi)]: 6
[(0,0,kırmızı),(0,0,mavi),(0,0,kırmızı)]: 7
[(0,0,kırmızı),(0,0,kırmızı),(0,0,kırmızı)]: 8
[(0,0,yeşil),(0,0,yeşil),(0,0,mavi)]: 9
core.exception.AssertError@deneme(16662): Assertion failure

Yani tabloya 10 farklı nesne yerleşmiş; yani yanlış olmuş. Çünkü renklerin önemsiz olmalarını istiyorduk.

Programı '-version=hatasız' seçeneğiyle derlersek, Noktalar'ın toHash işlevi de etkinleşiyor ve bu sefer tabloda tek bir eleman bulunuyor. Çünkü Noktalar'ın toHash işlevi, diğer ikisi gibi rengi gözardı edecek şekilde yazılmış:

$ dmd deneme.d -version=hatasız -w
$ ./deneme
Bütün tablo:
[(0,0,kırmızı),(0,0,kırmızı),(0,0,mavi)]: 9

Özet: bu üç özel işlevin birisi bile tanımlanmışsa, her üçünün de uyumlu olarak tanımlanmaları gerekir.

Ali

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

March 31, 2011

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

Aslında burada sayı dizisinin tam ortasındaki değer alınarak da yapılabilir belki ama ben bilgisayara da tahmin ettirdim :)

import std.stdio;
import std.random;

void main()
{
   int sayı = uniform (1, 100);

   writeln ("1'den 100'e kadar bir sayı tuttum!...");

   int tahmin;
   int altAralık = 1;
   int üstAralık = 100;


   do
   {
       writeln ("Tahmininiz nedir? ");

       tahmin = uniform (altAralık, üstAralık);
       if (tahmin == altAralık)
           tahmin +=1;
       write ("Bilgisayar: Tahmin ediyorum: ");
       writeln ("[", altAralık, " - ", üstAralık, " ]");
       writeln (tahmin);


       if (sayı < tahmin) {
           write ("Tuttuğum sayı daha küçük; ");
           üstAralık = tahmin;

       } else if (sayı > tahmin) {
           write ("Tuttuğum sayı daha büyük; ");

           if ((üstAralık > tahmin) && (altAralık < tahmin))
               altAralık = tahmin;

           else if ((üstAralık > tahmin) && (altAralık > tahmin))
               üstAralık = tahmin;

       }

   } while (tahmin != sayı);

   writeln ("Buldun!");
   writeln ("Tuttuğum sayı ", sayı, " idi");

}

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

March 31, 2011

Alıntı (acehreli):

>
  • Alışık olduğum kodlama standartlarında işlevlerden sonra boşluk bırakılmıyor: 'writeln("a")' ama parantezli deyimlerden sonra bırakılıyor: 'if (koşul)'. Hiç önemli değil tabii ama birisine alışınca hemen göze batıyor... :)

Aslında böyle inci gibi dizi dizi kod yazanlara hasta oluyorum :)

Ama bir türlü kendimi bir kodlama standardına adapte edemedim sanırım. Yukarda bahsettiğiniz boşluklara pek dikkat etmiyorum ama özellikle dizilerde illa bir boşluk bırakıyorum. Hatta bazı programlara tekrar boşluk koyduğum oldu :)

   auto bazıSayılar = [2, 3, 5, 7, 9, 11, 13, 15];

   auto dizi = new int [100];
   auto kopya = dizi.dup;

Alıntı (acehreli):

>
  • uniform()'u daha doğal kullanabiliriz sanki. Herhalde uniform() ikinci parametre değerini seçmediği için tahmin'i bir arttırman gerekmiş, değil mi?

Aslında gene alttaki örneği düşünürsek varsayılan davranışla [10, 20) aralığında bazen bir kaç defa 10 tahmin edebiliyor.

Alıntı (acehreli):

>
> uniform!"()"(10, 20)    // seçtiği değerler: 11-19
> uniform!"[)"(10, 20)    // seçtiği değerler: 10-19 (varsayılan davranış)
> ```

>
Benim istediğim ise (10, 20) aralığında birinci örnekteki gibi 11-19 arasından seçim yapması. Aslında dökümanlarına da baktım ama ya böyle bir örnek yoktu ya da ben görememiş olabilirim.

Yorumlar için teşekkürler :)

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

Çok güzel. :) Bilgisayara akıllı işler yaptırmak her zaman zevkli oluyor. Paylaştığın için teşekkürler.

Söylediğin gibi: tahmin olarak orta değeri kullanmak en hızlısı olur. O zaman baştaki aralıkta N sayı varsa, en fazla log(N) denemede bulunur. (Yani 1024 değer için 10 deneme, 65536 için 16 deneme gibi.)

Notlar:

  • altAralık yerine altDeğer gibi bir isim daha mı uygun olur acaba? Çünkü tek aralık ve iki sınır değer var.

  • Alışık olduğum kodlama standartlarında işlevlerden sonra boşluk bırakılmıyor: 'writeln("a")' ama parantezli deyimlerden sonra bırakılıyor: 'if (koşul)'. Hiç önemli değil tabii ama birisine alışınca hemen göze batıyor... :)

  • uniform()'u daha doğal kullanabiliriz sanki. Herhalde uniform() ikinci parametre değerini seçmediği için tahmin'i bir arttırman gerekmiş, değil mi? (Yine de garip geliyor.) Bence şu kullanım daha doğal: 'uniform(altAralık, üstAralık + 1)'. Böylece hep yasal değerlerden seçer.

Not: Aslında uniform()'un şablon parametresi, işlev parametrelerinin aralığa dahil edilip edilmediğini belirleyebiliyor:

uniform!"()"(10, 20)    // seçtiği değerler: 11-19
uniform!"[)"(10, 20)    // seçtiği değerler: 10-19 (varsayılan davranış)
uniform!"(]"(10, 20)    // seçtiği değerler: 11-20
uniform!"[]"(10, 20)    // seçtiği değerler: 10-20
  • Belki de bir önceki konu nedeniyle 'else if (sayı > tahmin)' bloğu sanki fazla karmaşık olmuş. 'altAralık = tahmin;' yeterlidir herhalde, değil mi? Hmmm... Aslında 'altAralık = tahmin + 1' daha doğru olur; çünkü tahmin'in doğru olmadığını da biliyoruz (Aynı nedenle 'if (sayı < tahmin)' bloğunda da 'üstAralık = tahmin - 1' olmalı)

  • En son olarak, benim söylediklerimi uygulayınca şöyle pek de akıllıca olmayan davranışlar da olabiliyor: :)

'...
Bilgisayar: Tahmin ediyorum: [32 - 32 ] <-- !
32
Buldun!
Tuttuğum sayı 32 idi'

Ali

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

April 29, 2011

Alıntı (acehreli:1277307019):

>

Alıntı (canalpay):

>
  1. Soru : sayılar ve çiftler dizinide aynı nesneye erişim sağlıyor. Burada çiftler sayıların dilimi.

Doğru.

Ben de bu sınıflarla ilgili soruların çözümünü yapmaya çalıştım. Bana açıkça biraz karışık ve zor geldi :)
Hatta DigitalMars'ta sorulacak türden bir soru galiba. Kodu biraz daha basitleştirmeye çalıştım.

   Sayı[] sayılar = null;
   Sayı[] çiftler = null;

   auto sayı = new Sayı(0);
   sayılar ~= sayı;

   // referansı sayılar[0] da tutuyoruz
   assert(sayılar[0] is sayı);

   çiftler ~= sayı;
   assert((sayı is sayılar[0] && sayı is çiftler[0]));

   // burası biraz karışık gibi :(
   // şimdi yeni bir nesne oluşturduk ve ilk
   // referansımızı bu yeni nesneyi gösterecek şekilde değiştirdik
   // o zaman sayılar[0] ve çiftler[0]'da tutulan referansların
   // geçerli bir referans olduğunu söyleyebilirmiyiz

   sayı = new Sayı(1);


   swap(sayılar[0], çiftler[0]);
   // referansları yerdeğiştirsek bile her ikisi de aynı nesneyi
   // gösteriyor

   assert(sayılar !is çiftler); // bence bu çiftler sayıların dilimi değil
   assert(sayılar == çiftler);  // ama içerikleri eşit şimdilik

Benim buradan anladıklarım 'sayılar' ve 'çiftler' aslında nesneleri değil referansları tutuyor. Bana sanki gene 'çiftler' 'sayılar''ın dilimi değil gibi geldi.

Bir de asıl önemli nokta 'sayı = new Sayı(1);' dediğimizde 'sayılar[0]' ve 'çiftler[0]''da kalan referansların geçerli bir referans olduklarını söyleyebilirmiyiz.

Bana burdaki işlem şu örnekteki gibi geliyor.

class A {
   int x = 42;
}

unittest {
   auto a1 = new A;
   assert(a1.x == 42);
   auto a2 = a1;       // yeni bir A nesnesi oluşturulmaz sadece A nesnesi
                       // a2 tarafından da gösterilmeye başlanır
   assert(a1 is a2);
   assert(a1 == a2);

   a2.x = 100;
   assert(a1.x == 100);
}

Yani dolaylı olarak bir nesneyi gösteren iki referans oluşmasına izin verdik. Ama böyle bir durumda örnekteki gibi beklenmeyen bir şekilde referans değiştiği zaman uygulamanın bir yerinde olan sürpriz değişiklikler diğer tarafına da yansıyabilir.

Kısacası demek istediğim aslında aynı nesneyi gösteren birden fazla referans olmasına izin vermememiz gerekmiyor mu.

Alıntı (acehreli:1277307019):

>

Alıntı:

>
  1. Artık aynı nesneye değil farklı nesneye erişim sağlıyor.

Evet.

(Kolaylık olsun diye, şuradaki 2 numaralı problemle ilgili konuşuyoruz: http://ddili.org/ders/d/siniflar.html)

İkinci foreach içinde

> eleman = new Sayı(uniform(0, 20));
> ```

>
> yazınca, 'eleman' o noktada yeni bir Sayı nesnesine erişim sağlamaya başlıyor. Bu 'eleman''ın yaşamı çok kısa: yaşamı, döngünün kapama parantezinde son buluyor.
>

Evet haklısınız. Ama burada yaptığımız aslında 21'inci 22'inci 'Sayı' nesnelerini oluşturup 'sayılar' dizisindeki referansların bu yeni nesneleri göstermesini sağlamak değil mi. Ama amacımız 'sayılar' dizgisindeki değerleri 'çiftler' dizgisindeki değerler değişmeden 5 arttırmak olsaydı sanırım kolay bir şekilde yapamazdık.

Dediğim gibi bu örnek bana hem biraz karışık, hem de derste anlatılanları tam pekiştiremiyor gibi geldi  :huh:

O yüzden daha ilginç ve daha basit bir örnekle değiştirilmesini öneriyorum :)

Teşekkürler..

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

Alıntı (acehreli):

>

Alıntı (erdem):

>
> >     Sayı[] sayılar = null;
> >     Sayı[] çiftler = null;
> >
> >     auto sayı = new Sayı(0);
> >     sayılar ~= sayı;
> >
> >     // referansı sayılar[0] da tutuyoruz
> >     assert(sayılar[0] is sayı);
> >
> >     çiftler ~= sayı;
> >     assert((sayı is sayılar[0] && sayı is çiftler[0]));
> > ```

> Alıntı:
> >
> >
sayı = new Sayı(1);
>

Şimdi sayı yeni bir nesneye erişim sağlıyor. Baştaki nesne hâlâ geçerli:

'
--> nesne(1)
|
sayı -- --> nesne(0)
|
sayılar[0]--|
|
çiftler[0]--
'

Evet ben de tam olarak böyle düşünüyorum.

http://i53.tinypic.com/11ae2z5.jpg

Alıntı (acehreli):

>

Alıntı:

>

Bana sanki gene 'çiftler' 'sayılar''ın dilimi değil gibi geldi.

Yukarıdaki kodlarda öyle olmasını düşündürecek bir şey göremiyorum.

İki dizi arasında atama ya da bir kısmını gösterme gibi bir ilişki kurmamışız. O yüzden o iki dizi tamamen ayrı diziler olarak karşımıza çıkmaz mı :)

import std.stdio;

void main () {

   auto a = ["merhaba", "dünya"];
   string b[] = a;
   assert (a is b);            // a ve b aynı sınırlara sahip
   assert (a == b);

   b = a.dup;                  // şimdi kopyalıyoruz
   assert (a == b);            // konumları farklı da olsa a ve b eşit

   assert (a !is b);            // her ne kadar içerikleri eşit olsa da a
                               // ve b artık birbirinden farklı

}

Örneğin daha önceki mesajdaki

assert(sayılar !is çiftler);

ifadesinin de içerikleri aynı olduğu için eğer dilim olsaydı 'is' olmasını beklemezmiydik.

Alıntı (acehreli):

>

Dikkate alınacaktır. :)

Aslında örneğin kendisinden çok neler olup bittiğini anlamak biraz karışık galiba :)

Teşekkürler ..

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

April 29, 2011

Alıntı (acehreli):

>

Alıntı (erdem):

>

Alıntı (acehreli):

>

Alıntı:

>

Bana sanki gene 'çiftler' 'sayılar''ın dilimi değil gibi geldi.

Yukarıdaki kodlarda öyle olmasını düşündürecek bir şey göremiyorum.

İki dizi arasında atama ya da bir kısmını gösterme gibi bir ilişki kurmamışız. O yüzden o iki dizi tamamen ayrı diziler olarak karşımıza çıkmaz mı :)

Hmmm... Bugüne kadar kullandığım dilde eksiklikler olduğunu şimdi farkediyorum: Ben bir dilimin "başka dilimin erişim sağladığı elemanlara erişim sağladığını" söyleyince, bu doğal olarak yukarıdaki sayılar ve çiftler'e de uyuyor. Çünkü ikisinin de ilk elemanları aynı nesneye erişim sağlıyorlar. Bu açıdan bakınca birisi ötekinin dilimi gibi anlaşılabilir. Ama aslında farklılar; ilgisiz bir durum.

Yok ben böyle anlamadım. Benim kasdettiğim daha önceki mesajlardaki
Alıntı:

>

Burada çiftler sayıların dilimi.

ifadesiydi.

Alıntı (acehreli):

>

Dilim olabilmek için, başka bir dizinin veya dilimin erişim sağladıkları elemanların ardışık bir bölümüne erişim sağlamak gerekir. Elemanları teker teker atayarak aynı sonuca ulaşılması onları bu konuda ilişkilendirmez.

Aynen katılıyorum.

Alıntı (acehreli):

>

D dilimlerinin iki üyeleri olduğunu biliyoruz. Sözde bir tanımları şöyle olabilir:

> struct __Dilim
> {
>     size_t length; // (aslında bu bir @property'dir)
>     size_t ptr;
> }
> ```

>

Bunun gerçeklemesini daha önce ben size sormuştum :)

Alttaki kodda da benim düşündüğümün aynısı. Tek farkla ki ilk örnekte ben dilim0'ın dizi olduğunu ve sadece dilim1'in dilim olduğunu düşünüyorum. İkinci örnekte ise bazı elemanları aynı nesneleri gösteren referanslar olsa da birbirinden tamamen bağımsız diziler olarak düşünüyorum. Bilmiyorum yanlış mı düşünüyorum.

Zaten sizin de belirttiğiniz gibi sayılar çiftler örneğinde ya da ikinci örnekte

assert(sayılar.ptr != çiftler.ptr);

bir tanesinin diğerinin dilimi olabilmesi için yukarıdaki ifadesinin '==' olması gerekiyordu.

Ben şöyle düşünüyorum. Eğer iki tanesinin uzunluğu eşitse ve 'is' koşulunu sağlıyorsa aynı sınırlara sahip.


Tüm bu konuştuklarımızdan nesneleri gösteren referansları sorunsuz bir şekilde dizilerde saklayabileceğimizi anlıyorum. İlk planda acaba bu referanslar geçersiz hale gelirse sonra çöp toplayıcı çalışıp belleği geri verebilir mi gibi değişik fikirler gelmişti. (İtiraf ediyorum bu nedenle bu soruyu sordum! :D )

Ama D de öyle serseri gösterge *dangling pointer* gibi durumlar oluşmadığı gibi referanslarda da problem olmuyor sanırım. Biraz da C++'nin sınıf yapısı ile D'nin ki sizin de bahsettiğiniz gibi farklı. Gene C++'de akıllı gösterge sınıfları ile normal sınıfların bellek yönetimini yapabiliyorduk.

Yanıtlar için çok teşekkürler! :)

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

Alıntı (acehreli):

>

dilim0 da dilim, çünkü dinamik dizilerle dilimler aynı şey:

>     int[] dilim0 = [ 11, 12, 13 ];
>     int[] dilim1 = dilim0;
> ```

>
> O kodda üç şey var:
>
> - yan yana üç int
> - onlara erişim sağlayan dilim0
> - onlara erişim sağlayan dilim1
>
> Yani dilim0 ile dilim1'in iç yapıları aynı.
>

Çok haklısınız. Orada iki tane dizi nesnesi bir tane dizi var. Ben daha önce şeklini de çizmeme rağmen karıştırmışım  :huh:

<http://i53.tinypic.com/28u075e.jpg>

Bu arada umarım sizi fazla zorlamamışımdır :)

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

Alıntı (erdem):

>
>     Sayı[] sayılar = null;
>     Sayı[] çiftler = null;
>
>     auto sayı = new Sayı(0);
>     sayılar ~= sayı;
>
>     // referansı sayılar[0] da tutuyoruz
>     assert(sayılar[0] is sayı);
>
>     çiftler ~= sayı;
>     assert((sayı is sayılar[0] && sayı is çiftler[0]));
> ```


Bu noktada tek nesne (new ile oluşturulmuştur ve isimsizdir) ve ona erişim sağlayan üç *değişken* var:

'
sayı ----------> nesne(0)
           |
sayılar[0]--|
           |
çiftler[0]--
'

Alıntı:
>
>
// burası biraz karışık gibi :(
// şimdi yeni bir nesne oluşturduk ve ilk
// referansımızı bu yeni nesneyi gösterecek şekilde değiştirdik
// o zaman sayılar[0] ve çiftler[0]'da tutulan referansların
// geçerli bir referans olduğunu söyleyebilirmiyiz

sayı = new Sayı(1);

Şimdi sayı yeni bir nesneye erişim sağlıyor. Baştaki nesne hâlâ geçerli:

'
--> nesne(1)
|
sayı -- --> nesne(0)
|
sayılar[0]--|
|
çiftler[0]--
'

Alıntı:

>
>
>
>     swap(sayılar[0], çiftler[0]);
>     // referansları yerdeğiştirsek bile her ikisi de aynı nesneyi
>     // gösteriyor
>
>     assert(sayılar !is çiftler); // bence bu çiftler sayıların dilimi değil
>     assert(sayılar == çiftler);  // ama içerikleri eşit şimdilik
> ```

> Benim buradan anladıklarım 'sayılar' ve 'çiftler' aslında nesneleri değil referansları tutuyor.

Doğru. Çünkü onlar Sayı dizileri. Sayı da bir class olduğu için zaten referanstır. Yani elimizde referans dizileri var.

Alıntı:
> Bana sanki gene 'çiftler' 'sayılar''ın dilimi değil gibi geldi.

Yukarıdaki kodlarda öyle olmasını düşündürecek bir şey göremiyorum.

Alıntı:
>
> Bir de asıl önemli nokta 'sayı = new Sayı(1);' dediğimizde 'sayılar[0]' ve 'çiftler[0]''da kalan referansların geçerli bir referans olduklarını söyleyebilirmiyiz.

Evet.

Alıntı:
>
> Bana burdaki işlem şu örnekteki gibi geliyor.
>

class A {
int x = 42;
}

unittest {
auto a1 = new A;
assert(a1.x == 42);
auto a2 = a1; // yeni bir A nesnesi oluşturulmaz sadece A nesnesi
// a2 tarafından da gösterilmeye başlanır
assert(a1 is a2);
assert(a1 == a2);

a2.x = 100;
assert(a1.x == 100);

}

>

Yani dolaylı olarak bir nesneyi gösteren iki referans oluşmasına izin verdik. Ama böyle bir durumda örnekteki gibi beklenmeyen bir şekilde referans değiştiği zaman uygulamanın bir yerinde olan sürpriz değişiklikler diğer tarafına da yansıyabilir.

Kesinlikle. Sınıf nesnelerinin kopyalanmalarını istiyorsak dizilere de benzesin diye 'dup()' isminde bir üye işlev yazmalıyız.

Ama uygulamada çoğunlukla D'nin (ve Java'nın) sınıflarının referans özellikleri gerekiyor. Ben C++ yazarken bile hemen hemen her sınıfı zaten D'deki gibi kullanıyorum:


// C++ kodu

// Sinifin tanimi; bu, D'deki "isimsiz nesne" gibi oluyor
class BirSinif {/* ... */};

// Referans türü; bu, D'deki "sınıf değişkeni" gibi oluyor
typedef boost::shared_ptr<BirSinif> BirSinifPtr;

// Nesne yapma işlev(ler)i; bu, D'deki new gibi oluyor
BirSinifPtr new_BirSinif(/* bir kurucusunun aldığı parametreler */)
{
   return BirSinifPtr(new BirSinif(/* bir kurucusunun aldığı parametreler */));
}

BirSinifPtr new_BirSinif(/* başka kurucusunun aldığı parametreler */)
{
   return BirSinifPtr(new BirSinif(/* başka kurucusunun aldığı parametreler */));
}

Ondan sonra program içinde hemen hemen her yerde BirSinifPtr'lar kullanıyoruz.

(Not: boost::shared_ptr yerine boost::intrusive_ptr daha hızlıdır ama ona geçmeyi gerektirecek bir durumla karşılaşmadık.)

Alıntı:

>

Kısacası demek istediğim aslında aynı nesneyi gösteren birden fazla referans olmasına izin vermememiz gerekmiyor mu.

Duruma göre değişir. Kendi deneyimlerim doğrultusunda D'nin (ve Java'nın) seçtiği yöntemin çoğu duruma uygun olduğunu düşünüyorum.

Alıntı:

>

Dediğim gibi bu örnek bana hem biraz karışık, hem de derste anlatılanları tam pekiştiremiyor gibi geldi :huh:

O yüzden daha ilginç ve daha basit bir örnekle değiştirilmesini öneriyorum :)

Dikkate alınacaktır. :)

Ali

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

April 29, 2011

Alıntı (erdem):

>

Alıntı (acehreli):

>

Alıntı:

>

Bana sanki gene 'çiftler' 'sayılar''ın dilimi değil gibi geldi.

Yukarıdaki kodlarda öyle olmasını düşündürecek bir şey göremiyorum.

İki dizi arasında atama ya da bir kısmını gösterme gibi bir ilişki kurmamışız. O yüzden o iki dizi tamamen ayrı diziler olarak karşımıza çıkmaz mı :)

Açık olamamışım: kodlarda zaten sayılar ve çiftler'in dilim olduklarını düşündürecek bir şey olmadığını söylemek istedim.

Hmmm... Bugüne kadar kullandığım dilde eksiklikler olduğunu şimdi farkediyorum: Ben bir dilimin "başka dilimin erişim sağladığı elemanlara erişim sağladığını" söyleyince, bu doğal olarak yukarıdaki sayılar ve çiftler'e de uyuyor. Çünkü ikisinin de ilk elemanları aynı nesneye erişim sağlıyorlar. Bu açıdan bakınca birisi ötekinin dilimi gibi anlaşılabilir. Ama aslında farklılar; ilgisiz bir durum.

Dilim olabilmek için, başka bir dizinin veya dilimin erişim sağladıkları elemanların ardışık bir bölümüne erişim sağlamak gerekir. Elemanları teker teker atayarak aynı sonuca ulaşılması onları bu konuda ilişkilendirmez.

D dilimlerinin iki üyeleri olduğunu biliyoruz. Sözde bir tanımları şöyle olabilir:

struct __Dilim
{
   size_t length; // (aslında bu bir @property'dir)
   size_t ptr;
}

Şimdi başka bir dilimden ilklenen şu koda bakalım:

class Sınıf
{}

void main()
{
   Sınıf[] dilim0;

   foreach (i; 0 .. 4) {
       dilim0 ~= new Sınıf;
   }

   Sınıf[] dilim1 = dilim0[0..2];

   assert(dilim0.ptr == dilim1.ptr);
   assert(dilim0.length == 4);
   assert(dilim1.length == 2);
   assert(dilim0 !is dilim1);
   assert(dilim0 != dilim1);
}

Oradaki iki dilimin durumu şu:

'
nesne nesne nesne nesne
^ ^ ^ ^
| | | |
================================
o o o o
================================
^
|
dilim0.ptr -----------|
dilim0.length == 4 |
|
dilim1.ptr -----------
dilim1.length == 2
'

Yani ikisi de bellekte yan yana duran elemanların başındakini gösteriyorlar. dilim1, bunlardan yalnızca ilk ikisinin yasal olduğunu düşünüyor. dilim0 da ilk dördünün... Bunun dışında bir şey bilmiyorlar.

Programı şimdi sayılar ve çiftler örneğine benzetmek için değiştiriyorum. Bu sefer sınıf değişkenlerini her iki dilime de ekleyeceğiz. Bu sefer ilk assert'ün de değiştiğine dikkat edin:

class Sınıf
{}

void main()
{
   Sınıf[] dilim0;
   Sınıf[] dilim1;

   foreach (i; 0 .. 4) {
       Sınıf değişken = new Sınıf;
       dilim0 ~= değişken;

       if (i < 2) {
           dilim1 ~= değişken;
       }
   }

   assert(dilim0.ptr != dilim1.ptr);   // <-- aynı değiller
   assert(dilim0.length == 4);
   assert(dilim1.length == 2);
   assert(dilim0 !is dilim1);
   assert(dilim0 != dilim1);
}

Şimdi durum şöyle:

'
dilim0.ptr -----------
dilim0.length == 4 |
v
================================
o o o o
================================
| | | |
v v v v
nesne nesne nesne nesne
^ ^
| |
==============
o o
==============
^
|
|
dilim1.ptr -----------
dilim1.length == 2
'

Sonuçta yine aynı nesnelere erişiliyor ama dilimlerde bulunan referanslar farklı.

Ali

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