February 12, 2023

Merhaba,

Ne zamandır ertelediğim ve tam kafamda oturtamadığım, bir dizge sarması olan MyString için (op: "~") işleçinin aşırı yüklemelerini yapayım dedim; pazar pazar sıkıntıdan, malum üzüntüden...:(

Paylaştığım 70 küsür satırın özeti şu:

  1. En az 2 + 1 yükleme tanımlamaya ihtiyacınız var ve
  2. Bunlar ya 2 adet opBinary() ile bir opBinaryRight() olmalı veya tersi aşağıdaki gibi tek opBinary() daha anlamlı.

Çünkü D derleyicisi, her satırı aşağıdan yukarı ve satırları da sağdan sola yorumlar. Dolayısıyla dizgeleri birbiri ile birleştirirken en sağdaki, işleçin solundakini işlev parametresi olarak alır ve siz de bu kurala uyarak işlevinizi kodlarsınız.

void main()
{
  auto merhaba = new MS("Merhaba");
  auto sana = new MS("sana");
  auto ey = new MS("ey");

  auto kelimeler = [ merhaba, sana, ey ];
  foreach(kelime; kelimeler)
  {
    kelime ~= " ";/*
    kelime.writeln;//*/
  }/*
  yukarıdaki opOpAssign() overloading'in gerçeklemesidir ve
  dizge sonlarına boşluk karakteri ekler, işte kanıtı: */
  import std.range : back;
  assert(merhaba.data.back == ' ');
  assert(merhaba.data == kelimeler[0].data);
  import std.stdio;
  kelimeler.writeln; // [Merhaba , sana , ey ]

  /* GERÇEKLEMELER: 
   ****************
   1. Aynı tür (MyString) ile
   2. Farklı tür (string) ile 
   opBinaryRight() overloading'in 1. gerçeklemesi ve eşdeğeri:*/

  auto cm1 = merhaba ~ sana ~ ey; /*
  auto tmp = new MS(sana.data ~ ey.data);
  auto cm1 = new MS(merhaba.data ~ tmp.data);//*/
  cm1.writeln; // Merhaba sana ey

  // opBinary() overloading'in gerçeklemesi:
  auto cm2 = cm1 ~ "D";
  cm2.writeln; // Merhaba sana ey D
  
  /* opBinaryRight() overloading'in 2. gerçeklemesi:*/
  auto cm3 = "=> " ~ cm2;
  cm3.writeln; // => Merhaba sana ey D

  merhaba ~= sana;
  merhaba.writeln; // Merhaba sana
  string str = cast(string)merhaba;
  assert(str == merhaba.data); // -test-ok-
}

alias MS = MyString;
class MyString
{
  string data;
  this(string s) { this.data = s; }

  alias opCast = toString;
  override string toString () const => data;

  MS opOpAssign (string op:"~")(string s) {
    this.data ~= s;
    return this;
  }
  
  MS opOpAssign (string op:"~")(MS that) {
    this.data ~= that.data;
    return this;
  }
  //MS opBinary (string op:"~")(MS that) => new MS(this.data ~ that.data); /*
  MS opBinaryRight (string op:"~")(MS that)
    => new MS(that.data ~ this.data);//*/
  
  MS opBinary (string op:"~")(string s)
    => new MS(this.data ~ s);

  MS opBinaryRight (string op:"~")(string s)
    => new MS(s ~ this.data);
}

Son söylediğim, sağdan sola olayını haksız çıkarabilecek konuyu vurgulamalıyım. Bunun için yukardaki son 4 işleve (overloading function) bakmalısınız. Evet, yeşil renkli olan gizlenen dahil. Onun başındaki yorum (//) işaretini kaldırdığınızda açılır ve altındaki otomatik gizlenir...

Göreceksiniz ki kod yine aynı sonucu üretecek. Çünkü birleştirilen dizgeleri, meşhur Snake oyununa benzetin (malum ~ işleçi bir yılana benziyor), yılan soldan yemeye/büyümeye başlıyor. Zaten teorik olarak 2 olasılık var, bunu kod içine gömdüm, tekrarlarsak:

  1. Aynı tür (MyString) ile
  2. Farklı tür (string) ile 

Özetle bu ister soldan, ister sağdan birleşerek devam etsin, işlevi doğru yazdıktan sonra bir şey değişmiyor. Bir de bu üçlüye opCast() ile birlikte iki de opOpAssign() eklerseniz tadından yenmez.

Dip Not: Yukardaki satırları yazıyorken biraz kafa karıştırma pahasına kodların tamamını paylaşmaya karar verdim. Dolayısıyla 2 x 3 yükleme ile kendi türlerinize benzer başka bir tür ile pekala tastamam etkileşime girebilir ya da dönüştürebilirsiniz. Zaten asıl tür string olduğundan opCast'i alias ile toString'e bağlamak yeterli.

Hoşça kalın.