Forum: Ders Arası RSS
İşleç yükleme
Sayfa:  1  2  sonraki 
erdem (Moderatör) #1
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: İşleç yükleme
struct Vector2
{
    float x, y;
    
    this (float x, float y)
    {
        this.x = x;
        this.y = y;
    }
 
    /*
      operatörlere farklı görev yükleme
    */
    Vector2 opMul(ref const Vector2 soldaki, float sağdaki)
    {
        // ?? Vector2 sonuç(soldaki)
        Vector2 sonuç = soldaki;
        sonuç *= sağdaki;
        return sonuç;
    }
 
    Vector2 opMul(float soldaki, ref const Vector2 sağdaki)
    {
        Vector2 sonuç = sağdakih;
        sonuç *= soldaki;
        return sonuç;
    }
    
    Vector2 opMulAssign(ref const float sağdaki)
    {
        x *= sağdaki;
        y *= sağdaki;
        return this;
    }
    
      
    Vector2 opAddAssign(ref const Vector2 sağdaki)
    {
        x += sağdaki.x;
        y += sağdaki.y;
 
        return this;
    }
    
    /*
      bu vektöre dik bir vektör döndürür
     */
    Vector2 dik() const
    {
        return Vector2(y, -x);
    }
 
    /*
      skaler çarpımı (dot product) hesaplar
     */
    float skalerÇarpım(ref const Vector2 v2) const 
    {
        return x * v2.x + y * v2.y;
    }
 
    /*
      bu vektörün tersi olan vektörü bulur
    */
    Vector2 tersiniBul() const 
    {
        return Vector2(-this.x, -this.y);
    }
    
    /*
    
    void yansıt(ref const Vector2 normal) 
    {
        this += 2.0 * this.skalerÇarpım(normal) * normal.tersiniBul();
    }
    */
}
 
unittest
{
    /*
        bu şekilde atama yapamıyoruz sanırım
        Vector2 ikinci(Vector2(1, 1);
    */    
    auto ilk = Vector2(2, 2);
    auto ikinci = Vector2(-3, -3);
    ilk = ilk.tersiniBul();
    assert(ilk == Vector2(-2, -2));
    float sayı = -1.0;
    ilk *= sayı;
    assert(ilk == Vector2(2, 2));
    ilk = 2.0f * ikinci;
    
}
 
void main() 
{
}
Yukarıdaki birim testinde en alttaki ifade no property 'opMul_r' for type 'float' diye bir hata veriyor. Ayrıca C++'de olduğu gibi () arasında atama yapamıyoruz galiba. Gene yansıt işlevi de yorum kaldırılınca hata veriyor.

Digitalmars'ın dökümanlarına baktım ama gene çoğu zaman olduğu gibi pek bir şey anlamadım <_<

Biraz da tembellik ederek daha TDLP'ye bakmadan yazdım :)
erdem (Moderatör) #2
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Tamam şimdi TDPL'ye bakınca buldum. Baya ilginçmiş.

 Andrei örneklerde de şablon kullanmış :) Aslında işe yarayan bir özellik.

Ama şimdilik basitçe şunun gibi olması gerekiyor sanırım. Örneğin += operatörünü yüklediğimiz zaman:
import std.stdio;
 
struct Vector2
{
    float x, y;
    
    this (float x, float y)
    {
        this.x = x;
        this.y = y;
    }
    
    ref Vector2 opOpAssign(string op)(Vector2 sağdaki)
 
    if (op == "+") {
        x += sağdaki.x;
        y += sağdaki.y;
        return this;
    }
}
 
void main() 
{
    auto ilk = Vector2(2, 2);
    ilk += Vector2(3, 3);
    assert(ilk == Vector2(5, 5));
    
}
Yukarda yazdığım örneklerin tamamı geçersiz hale gelmiş deprecated anladığım kadarıyla.. Bu yukarda  opOpAssign şeklinde yazdığım gruba atama operatörleri giriyor. a = b a += b ve a *= b gibi..
acehreli (Moderatör) #3
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #1
Ben sabah yazacak zaman bulamadım ve işe gelirken uğraştım.

erdem:
    Vector2 opMul(ref const Vector2 soldaki, float sağdaki)
    {
        // ?? Vector2 sonuç(soldaki)
        Vector2 sonuç = soldaki;
        sonuç *= sağdaki;
        return sonuç;
    }

O işlevlerin yalnızca float alması gerekiyor. Çünkü 'soldaki' nesne aslında 'this' nesne:

    ref Vector2 opMulAssign(const float sağdaki)
    {
        x *= sağdaki;
        y *= sağdaki;
        return this;
    }
 
    Vector2 opMul(float sağdaki) const
    {
        Vector2 sonuç = this;
        sonuç *= sağdaki;
        return sonuç;
    }

yansıt'la fazla uğraşmadım ama sağ tarafı ayırınca çalışıyor:

    void yansıt(ref const Vector2 normal)
    {
        auto eklenen = 2.0 * this.skalerÇarpım(normal) * normal.tersiniBul();
        this += eklenen;
    }

İşleç yüklemenin yeni yazımının kolaylığı, olabilen durumlarda birden fazla işlecin tek işlevle tanımlanabilmesi:

struct Vector2
{
    double x;
    double y;
 
    ref Vector2 opOpAssign(string işleç)(double sağdaki)
    {
        mixin("x " ~ işleç ~ "= sağdaki;");
        mixin("y " ~ işleç ~ "= sağdaki;");
        return this;
    }
 
    Vector2 opBinary(string işleç)(double sağdaki)
    {
        Vector2 sonuç = this;
        sonuç.opOpAssign!işleç(sağdaki);
        return sonuç;
    }
 
    Vector2 opBinaryRight(string işleç)(double soldaki)
    {
        return this.opBinary!işleç(soldaki);
    }
}

mixin garip oluyor ama yukarıdaki 3 tanım çoğu ikili işleç için galiba doğru. (Ama "double / Vector2" gibi durumlar tabii ki yanlış olur.)

Ali
erdem (Moderatör) #4
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Çok teşekkürler! :)

Artık birim testlerini uzun uzun yazarım. Bu arada birim testleri aslında tek tek her modül için ayrı yapmak daha mantıklı galiba. Öbür türlü benim yaptığım gibi çorba gibi oluyordu  :-D  Bir taraftan program çalışıyor bir taraftan birim testleri kendi aralarında takılıyorlar.

Bu gün rastgele gezerken Andrei'nin nasıl birim testleri yazdığını gördüm. Bu bana iyi bir fikir verdi:

http://bit.ly/mBUDn8
erdem (Moderatör) #5
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
import std.stdio;
 
struct Vector2
{
    float x, y;
    
    this (float x, float y)
    {
        this.x = x;
        this.y = y;
    }
    
    // vector2 * sayı
    Vector2 opBinary(string işleç)(const float sağdaki)
    if (işleç == "*")
    {
        auto sonuç = this;
        this *= sağdaki;
        return this;
    }
 
    // sayı * vector2
    Vector2 opBinaryRight(string işleç)(const float soldaki)
    if (işleç == "*")
    {
        return this.opBinary!(işleç)(soldaki);
    }
 
    /*
      atama işleçleri
    */
 
    // vector2 = vector2
    ref Vector2 opAssign(const ref Vector2 sağdaki)
    {
        x = sağdaki.x;
        y = sağdaki.y;
        return this;
    }
 
    // vector2 *= sayı
    ref Vector2 opOpAssign(string işleç)(const float sağdaki)
    if (işleç == "*") {
        x *= sağdaki;
        y *= sağdaki;
        return this;
    }
}
 
unittest
{
    auto birinci = Vector2(1, 2);
    auto ikinci = Vector2(3, 3);
    auto sayı = 4.0f;
 
    Vector2 sonuç = birinci *= 3;
    assert(sonuç == Vector2(3, 6));
    // BUG *
     assert(birinci == Vector2(1, 2));    
}
Burada bir hata var. Ama kafam karıştı galiba. Bir türlü nerede ilk değeri de değiştirdiğini göremedim  :huh:

Çok güzel çalışan programımız BUG kısmına gelince patlıyor!  :-D
erdem (Moderatör) #6
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
erdem:
    // vector2 * sayı
    Vector2 opBinary(string işleç)(const float sağdaki)
    if (işleç == "*")
    {
        auto sonuç = this;
        this *= sağdaki;
        return this;
    }

Sabahtan beri bunla uğraşıyordum. Bazen kafa basmıyor  :-D Burada kod this'i değiştiriyormuş. Doğrusu şu şekilde olacak:
        auto sonuç = this;
        sonuç *= sağdaki;
        return sonuç;
erdem:
    Vector2 sonuç = birinci *= 3;

Burada da doğal olarak birinci *= 3 birinciyi değiştirecek. Bunun doğrusu da sadece birinci *= 3; oluyor.
acehreli (Moderatör) #7
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Güzel! :)

Denemek için yazdığını görüyorum ve sen de bazılarını değiştireceksin ama bir kaç tabii ki kişisel görüşüm var:

- Ben özel bir nedeni yoksa float yerine double kullanıyorum. Tabii belki de en iyisi gerçekleştirme türünü ve hatta boyutu bile parametre olarak almaktır:

struct Vector(T, int boyut)
{
    T[boyut] elemanlar;
}

- Yapılar değer türleri olduklarından dil bize çok yardımcı oluyor; bizim bazı durumlarda özel işlemler yapmamıza gerek kalmıyor. Örneğin bu tür için opAssign(Vector2)'yi yazmaya aslında gerek yok

- Bu yüklemeler aslından birden fazla işleçle de kullanılabilir. Onun için şablon kısıtlamalarıyla küçük bir deney yaptım. std.algorithm.canFind, bir elemanın bir aralıkta bulunup bulunmadığını bildiriyor. Belki dışlama gereken durumlarda yararlı olabilir:

import std.algorithm;
// ...
    void birŞablon(/* ... */)
    if (!canFind("/%", işleç))
    {
        // ...
    }

Ali
erdem (Moderatör) #8
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
- Ben özel bir nedeni yoksa float yerine double kullanıyorum.

Aslında XNA da doğal sayı türü olarak float kullanılmış. Ben de konunun tam ayrıntılarını bilmiyorum ama sanki bir yerlerde GPU(Grafik İşlemci Birimi) float sayılar için optimize edilmiştir türünden bir şeyler okumuştum. O yüzden float kullanayım diye düşündüm.

acehreli:
Tabii belki de en iyisi gerçekleştirme türünü ve hatta boyutu bile parametre olarak almaktır:

struct Vector(T, int boyut)
{
    T[boyut] elemanlar;
}

struct Vector2(T, int boyut)
{
    T[boyut] elemanlar;
}
 
void main() 
{
    auto birVektör = Vector2!(float, 3)(4, 4);
}

Burda boyutun nasıl kullanılacağını anlamadım. Amacımız iki noktanın koordinatını tutmak olduğuna göre şöyle yazmazmıydık:

struct Vector2(T)
{
    T x, y;
}
 
void main() 
{
    auto başkaVektör = Vector2!(float) (2, 3);   
}

Aslında bunu düşündüm. Örneğin hem bir ushort hem de float türünde Vector2 gerekebiliyor. Ama bu şekilde her seferinde yazım biraz uzun olur ve kullanıcının kafasını karıştırır mı diye çekincede kaldım. Artık ilerde belki tamamen bu hale getirebilirim :)

acehreli:
- Yapılar değer türleri olduklarından dil bize çok yardımcı oluyor; bizim bazı durumlarda özel işlemler yapmamıza gerek kalmıyor. Örneğin bu tür için opAssign(Vector2)'yi yazmaya aslında gerek yok

Çok teşekkürler. Bunu öğrendiğim iyi oldu :)

acehreli:
Onun için şablon kısıtlamalarıyla küçük bir deney yaptım. std.algorithm.canFind, bir elemanın bir aralıkta bulunup bulunmadığını bildiriyor.

Bu kısmı pek anlamadım  :huh:  Biraz açabilirmisiniz.

Yorumlar için teşekkürler! :)
erdem (Moderatör) #9
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
erdem:
Ama bu şekilde her seferinde yazım biraz uzun olur ve kullanıcının kafasını karıştırır mı diye çekincede kaldım.

Ama alias kullanarak böyle kısaca yazılabiliyor :) En çok kullanılan değer Vector2 olduğu için böyle basit bir alias kullanarak yazılabiliyor. Ama alias şablonlarla problem çıkarır mı bilmiyorum.

struct Vector(T)
{
    T x, y;
}
 
alias Vector!(float) Vector2;
 
 
void main() 
{
    auto konum = Vector2 (2, 3);   
}
acehreli (Moderatör) #10
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #8
erdem:
float sayılar için optimize edilmiştir türünden bir şeyler okumuştum

Yani "geçerli bir nedeni" varmış. ;)

acehreli:
struct Vector(T, int boyut)
{
    T[boyut] elemanlar;
}
Burda boyutun nasıl kullanılacağını anlamadım.

alias'ı hatırlamışsın zaten...

Benim yaptığım, uzunluğu 'boyut' olan bir sabit dizi kullanmaktı. Böylece bütün işleç yüklemelerin şu kadar basit olabilir:

elemanlar[] *= sağdaki;

Ayrı ayrı x ve y yazmaya gerek yok. O işlem bütün elemanlara uygulanır. (Aslında son baktığımda bu konuda hatalar vardı. (Hatırlatma: http://ddili.org/ders/d/dilimler.html sayfasındaki "Bütün elemanlar üzerindeki işlemler" (Kendim de hatırladım: sabit uzunluklu dizilerde zaten hata yokmuş.))

acehreli:
std.algorithm.canFind, bir elemanın bir aralıkta bulunup bulunmadığını bildiriyor.

Biraz açabilirmisiniz.

Şablon kısıtlamalarında tek tek işleç == "*" yazmak zorunda kalmamak için canFind'ı düşündüm. canFind, aranan eleman aralıkta bulunduğunda true döndürüyor. Yani !canFind("/%", işleç) yazınca "işleç bölü veya yüzde karakteri değilse" anlamına geliyor. D'nin derleme zamanında işlev işletebilmesinin (CTFE) bir yararı daha. :) Yoksa şablon kısıtlamasında kullanamazdık.

Ama alias şablonlarla problem çıkarır mı bilmiyorum.

Hiçbir sorunu olmaz. alias, yazdığın türün ta kendisi yerine geçiyor. Yalnızca kullanışlı bir ad. (C'nin typeof'unun aynısı.)

Ali
acehreli (Moderatör) #11
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
alias, yazdığın türün ta kendisi yerine geçiyor. Yalnızca kullanışlı bir ad. (C'nin typeof'unun aynısı.)

Yanlış! alias, C'nin typedef'inin aynısıdır.

Ali
erdem (Moderatör) #12
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Teşekkürler! :)

Şimdilik son hali şunun gibi olmuş oldu:

http://bit.ly/mcEaFu
acehreli (Moderatör) #13
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Üç küçük not:

1)

    @property T uzaklık(ref const Vector v2) const
    {
        T yMesafesi = v2.y - y;
        T xMesafesi = v2.x - x;
        return sqrt(yMesafesi * yMesafesi + xMesafesi * xMesafesi);
    }

Benim anladığım kadarıyla, eğer parametre almıyorsa @property bu nesnenin bir niteliğini döndürür. Eğer parametre alıyorsa, atama işlemi ile bu nesnede bir değişiklik yapar. (Şuradaki "Atama işleci ile kullanılan nitelik işlevleri" başlığında bir örnek var: http://ddili.org/ders/d/nitelikler.html)

O yüzden bence uzaklık() @property olmasa daha uygun.

2) tersiniBul() yerine tersi() daha iyi bir isme benziyor. Hatta işte bu bir @property olabilir galiba. Çünkü v.tersi deyince v'nin bir niteliğini veriyor. (Senin neden Bul ile bitirdiğini anlıyorum: işlevlerin isimlerinin emir kipinde olması uygun.)

3) Aslında önceden olduğu gibi türün ismi Vector2 olmalı, çünkü iki noktası var. Yani "2'liğini" şu alias vermiyor:

alias Vector!(float) Vector2;

Ali
erdem (Moderatör) #14
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
(Şuradaki "Atama işleci ile kullanılan nitelik işlevleri" başlığında bir örnek var: http://ddili.org/ders/d/nitelikler.html)

O yüzden bence uzaklık() @property olmasa daha uygun.

2) tersiniBul() yerine tersi() daha iyi bir isme benziyor. Hatta işte bu bir @property olabilir galiba.

Zaten o dersi okuduktan  @property yapmıştım. Ama haklısınız bu değişiklikleri yaptım bile :)

acehreli:
3) Aslında önceden olduğu gibi türün ismi Vector2 olmalı, çünkü iki noktası var. Yani "2'liğini" şu alias vermiyor:

alias Vector!(float) Vector2;

Haklısınız ama hem mantıklı bir isim bulamadım. Hem de kodun her yerindeki Vector2'leri değiştirmem gerekecek. O yüzden şimdilik böyle yazdım.

Bu arada isterseniz github'da da mentor olarak yorum yazabilirsiniz :)

Ama tabi bu şekilde forumu takip eden herkes izlemiş oluyor.
Avatar
Salih Dinçer #15
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Herkes iyi haftalar dilerim...

Hafta sonuna bakarsak benim için çok karma karışık oldu! Bu işleç yükleme (operator overloading) olayı yeterince kafamı karıştırdı. Ancak kısmen yol aldığımı söyleyebilirim. Sormak istediğim tek bir şey var:

Acaba aşağıdaki örnekte görüldüğü üzere, yeni bir matematik formasyonu temeli oluştursak ama alıştığımız gibi; yani:
set += cast(int) 2; // cast'i değeri ifade etmek için kullandım lütfen cast olmadan düşünün... 
Örneğinde olduğu gibi kullanabilsek mümkün mü?
import std.stdio;
 
template Signs(string sign, T) {
    void Assign(ref T a, T b) {
        static if (sign == "+") a += b;
          else if (sign == "-") a -= b;
          else if (sign == "*") a *= b;
          else if (sign == "/") a /= b;
    }
}
 
struct newMath {
    int x;
 
    ref opOpAssign(string sign)(newMath value) {
        Signs!(sign, typeof(x)).Assign(x, value.x);
    }
}
 
void main() {
    auto set = newMath(1);
    for (int pic = 1; pic < 7; pic++) {
        set *= newMath(pic);
        writeln(set.x);
    }
}
Sevgiler, saygılar..
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Sayfa:  1  2  sonraki 
Forum: Ders Arası RSS
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-22, 02:44:40 (UTC -08:00)