Forum: D Programlama Dili RSS
Bir Garip Yığıt
A Strange Stack
Avatar
Salih Dinçer #1
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Bir Garip Yığıt
Merhaba,

Aşağıda bir garip yığıt yapısı ve hemen altında deneme kodu yer almakta...:)
Kısaca;
  • Bu yapıyı bir dizi gibi hayal etmeliyiz. Ama kuramsal olarak başlangıç ve bitiş değerleri sonsuz!
  • Başlangıç (daha doğrusu sol kenar boşluğu ~ offset), bizim tarafımızdan kurulurken belirleniyor.
  • Sağ tarafı (dizinin devamı) ise boşluk ve değerleri de her zaman sıfır olmak zorunda,
  • Çünkü bunlar (sol ve sağı veya alt ve üstü) sanal değerler ve olmayan bu değerlere erişilirken dizi gibi hata vermiyor...
  • Doğal olarak hata vermemesine karşın hiç bir değere de eşitlenemiyor...:)

Dedik ya bir garip yığıt ama yerine göre çok işe yarayabilir...
struct birGaripYığıt
{
    private:
        int[] stack;
        size_t _Index;
        size_t _Offset;
 
    this (size_t realLen, size_t diffLen = 0)
    {
        stack = new int[realLen];
        _Offset = diffLen;
        //_Index = index();
    }
 
    public size_t index() @property
    {
        return stack.length + _Offset -1;
    }
 
    public void set(size_t len, int data) @property
    {
        if((len >= _Offset) &
           (len <= index()))
        {
            stack[len - _Offset] = data;
        }
    }
 
    public int get(size_t len) @property
    {
        if((len >= _Offset) &
           (len <= index()))
        {
            return stack[len - _Offset];
        }
        return 0;
    }
    enum empty = false;
 
    void popFront()
    {
        _Index++;
    }       
 
    int front() 
    {
        return get(_Index);
    }
}
 
import std.stdio;   
void main() {
    int[] testArray = [ -3, -2, -1, 0, 1, 2, 3 ]; // 4 <-- SONRADAN EKLENEN VERİ
    // YAZILMAYAN VERİ -------------^  ^------- YAZILAN VERİ
    with(birGaripYığıt (10, 4))
    {
        index().writeln;               // kurucu parametre toplamlarının 1 eksiği
        foreach(index, data; testArray)
        {
            set(index, data);          // test dizisini yapıdaki diziye yerleştir
        }
        get(4).writeln;                // yapının gerçek ilk elemanını döndürür
        set(index(), 4);
        get(index()).writeln;          // yapının gerçek son elemanını döndürür
    }
}
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #2
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ı
Yani elemanların ortasında yazılabilen bir bölge var. Aslında bu bölgenin dışına da yazılabiliyor ama o işlemlerin etkileri yok.

  • İsimleri daha iyi seçebiliriz. Örneğin set() ve get()'in parametreleri 'len' değil de 'index' olacak galiba, değil mi? Çünkü sanırım onlara bir uzunluk değeri değil, bir konum değeri veriyoruz.

  • Bu konuyla tam ilgili değil ama InputRange işlevlerini bu tür üstünde tanımlamak yerine bunun bir aralığı üstünde tanımlamak daha iyi olur. Bunun nedeni, bu bir topluluk. Birisi elemanlar üzerinde gezindi diye eleman kaybetmemeliyiz. Tabii kullanım amacına göre öyle olmayabilir de. Ben topluluklarla aralıkların böyle bir ayrımları olması gerektiğini yakın zaman önce farketmiştim. Topluluk yerinde durmalı ve istendikçe elemanları üzerinde gezinmeyi sağlayan aralıklar döndürmeli.

Bu da opSlice() ile çok kolay. empty, front ve popFront() yerine yalnızca şu işlevi yazmak yeterli:

    int[] opSlice()
    {
        return stack;
    }

foreach döngüsü, opSlice() işleci bulunan türlerde bu işleci de kullanabilir. (Sanırım bunu daha önce konuşmuştuk: TDPL bu olanağı anlatıyordu ama dmd eskiden sağlamıyordu.)

Böylece aralık olarak kullanıldığında _Index'in değeri artmadığı için topluluk eleman kaybetmemiş olur. Ama dediğim gibi, yanlış anlıyor olabilirim; sen de tam da kullandıkça baştan kısalmasını istiyorsundur. :)

Ali
Avatar
Salih Dinçer #3
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
Bu da opSlice() ile çok kolay. empty, front ve popFront() yerine yalnızca şu işlevi yazmak yeterli:
    int[] opSlice()
    {
        return stack;
    }
opSlice ile döndürülen dizi sanal elemanlara sahip olmayacağı ve sonsuza gitmediği için bu yapıda işimize yaramayabilir.
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #4
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ı
Doğru! :)

Ali
Avatar
Salih Dinçer #5
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Doğru olmayan bir şey var ki o da take()'i kullanamamış olmam. Sebebini anlayamadım, şöyle bir hata alıyorum:
garipyığıt.d(61): Error: template std.range.take does not match any function template declaration
garipyığıt.d(61): Error: template std.range.take cannot deduce template function from argument types !()(birGaripYığıt,int)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #6
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ı
InputRange işlevlerini 'public' yapınca çalışıyor.

Ali
Avatar
Salih Dinçer #7
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Çok teşekkürler...
[Resim: http://img407.imageshack.us/img407/2017/smileythanks.jpg]
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Avatar
Salih Dinçer #8
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Bu iletiyi okuyanlar iki şey rica etsem yapar mısınız?

Şu garip yığıtı bir test etseniz çünkü önceki halinde gerçekten garip tavırları vardı...:)

Hoş zaten garip olduğu için henüz netleşmeye davranışları sergilediğini farkettim. Yani hata değil ama aşağıda yer alan yeni sürümü birlikte netleştirebilir miyiz?
struct Stack(T) {
    private:
        T[] stack;
        T empty_stack_cell;
        size_t _Index;
        size_t _Offset;
 
    this(size_t realLen, size_t diffLen = 0) {
        stack = new int[realLen];
        _Offset = diffLen;
    }
 
    size_t length() @property {
        return _Index;// - _Offset;
    }
 
    ref T set(size_t len) @property {
        _Index++;
        if((len >= _Offset) &
           (len <= _Index))
        {
            return stack[len - _Offset];
        } //else _Index--;
        return empty_stack_cell;
 
    }
 
    T get(size_t len) @property {
        if((len >= _Offset) &
           (len <= _Index))
        {
            return stack[len - _Offset];
        }
        return 0;
    }
}
 
import std.stdio;   
void main() {
    int[] testArray = [ -3, -2, -1, 0, 1, 2, 3 ]; // 4 <-- SONRADAN EKLENEN VERİ
    // YAZILMAYAN VERİ -------------^  ^------- YAZILAN VERİ
    with(Stack!int(16)) {
        length.writeln;
        foreach(i, data; testArray) {
            set(i) = data;          
        }
        length.writeln;
        set(length) = 4;
        stack.writeln;
        length.writeln;
    }
}
Ayrıca garibee bir kardeş geldi ismi gamsız, kod adı Infinity Stack..:D

Bu arkadaş henüz bebecik ve belki hala ana karnında! Doğmuş bile sayılmayan aşağıdaki örneği de denerseniz ikinci rica ettiğim şeyi görmüş olacaksınız. Yapmak istediğim dizi tükendiğinde index'i sona (arr.length - 1) alıyorum. Böylece ötekine benzer ama hiç bir zaman dolup taşmayan bir dizi elde ediyoruz. Sanırım oldu test ederseniz sevinirim...
struct iStack(T) {
    T[] arr;
    int len;
 
    this(int realLen) {
        arr = new int[realLen];
        len = realLen;
    }
 
    ref T equ() @property {
        if(--len < 0) len = arr.length - 1;
        return arr[len];
    }
}
 
import std.stdio;   
void main() {
    int[] testArray = [ -3, -2, -1, 0, 1, 2, 3 ]; // 4 <-- SONRADAN EKLENEN VERİ
 
    auto veri = iStack!int(5);
    foreach(data; testArray) {
        veri.equ = data;
        veri.arr.writeln;
    }
    foreach(i; 0..10) veri.equ.writeln;
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #9
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ı
(Daha önce yazdıklarımın tekrarı olabilir; unutmuşum, kusura bakmayın. :))

Bu sınırlı bir stack, öyle mi? Uzunluğunu baştan belirliyoruz ve daha fazla büyütemiyoruz. Kabul ama hatalı indekslerin sessizce gözardı edilmelerinin yararını ben hâlâ göremiyorum. 16 gibi bir boyut belirlemişsem 20 yok demektir. 20'ye yazmak hata atmalı çünkü hatamı bilmek önemli.

set ve get'in @property olmaları kafa karıştırıyor. @property işlevlerini parantezsiz kullanmayı bekleriz ama ikisi de len alıyorlar (aslında len değil de, indeks (veya konum) olsa daha doğru).

Şu kodda ne olmasını bekliyoruz?

    struct S
    {}
 
    auto s = Stack!S(10);
    s.set = 3;

Sondaki atamanın hiçbir etkisi yok çünkü s.set(3) çağrılıyor; o referans döndürüyor; ve döndürülen referans ile hiçbir şey yapılmıyor.

Ayrıca set her çağrıldığında _Index'in arttırılması da doğru gelmiyor. Aynı elemana arka arkaya set(3) yapsam eleman mı ekleniyor?

Bu türler belki de tamamen tasarımlarına uygun davranıyorlardır ama isimleri "stack" olunca ister istemez alıştığımız stack veri yapısı gibi davranmasını bekliyorum.

Kodla ilgili kişisel bir sorunum var: main içindeki işlemlerin bazıları with içinde oluşturulan isimsiz nesne üzerinde uygulanıyor. with, o nesneyi koddan çıkartılmasına neden olduğu için kodun okunmasını güçleşiyor.

Bir örnek: with'in içinde geçen testArray'in de başında hiçbir belirteç yok. Kodu okurken "aslında belki de nesne.testArray'dir" diye düşünülebilir. with, kodun bütünüyle tanınmasını gerektiriyor:

    with (birİfade()) {
        foo();    // birİfade üzerinde mi işliyor?
        bar();    // peki bu?
    }

O soruları yanıtlayabilmek için hem birİfade'nin döndürdüğü türü bütünüyle tanımam gerek. Aşağıdaki kodun with'ten daha üstün olduğunu iddia ediyorum:

    auto değer = birİfade();
    değer.foo();
    bar();

İşte şimdi foo()'nun birİfade()'nin döndürdüğü değer üzerinde işlediğini biliyorum. Hiçbir şey gizli değil.

Ali
Avatar
Salih Dinçer #10
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Estağfirullah hocam.

Asıl hata benda; bir şeyi bitirmeden hemen diğerine geçebiliyorum. Bu sizde de var mı bilmyorum ama bir şeyle çok muhatap olunca sıkılıyor insan. Araya başka bir şey sokunca takıldığı şeyden de kurtuluyor; sıkıntısında da! Tabi bu sefer özyinelemeli işlevler (recursive function) gibi iyi bir hafıza istiyor...:)

Eee, gülü seven dikenine katlanır veya hamama giren terler diyelim...

acehreli:
Ayrıca set her çağrıldığında _Index'in arttırılması da doğru gelmiyor. Aynı elemana arka arkaya set(3) yapsam eleman mı ekleniyor?

Bu soruyu bende sordum ve karar veremedim! Üstelik bu ilk sürümde (iletide) yoktu. Sonradan ekledim çünkü index() işlevi ile (şimdi length oldu...) uzunluğunu alıyordum ve bana offset (diğer kenardaki dikkate alınmayan boşluk) ile toplayıp sanal uzunluk değerini veriyordu. Eee, haliyle diziye yazılan son elemanın konumunu öğrenemiyordum. Sanırım burada ciddi bir index ile length karmaşası var...:)

acehreli:
Kodla ilgili kişisel bir sorunum var: main içindeki işlemlerin bazıları with içinde oluşturulan isimsiz nesne üzerinde uygulanıyor. with, o nesneyi koddan çıkartılmasına neden olduğu için kodun okunmasını güçleşiyor.

Yerden göğe kadar hakkın var, işte o yüzden son yığıtımızda (Gamsız) bunu kullanmadım. Sanırım with() kullanımını abartmamak gerekiyor.

acehreli:
Bu sınırlı bir stack, öyle mi? Uzunluğunu baştan belirliyoruz ve daha fazla büyütemiyoruz. Kabul ama hatalı indekslerin sessizce gözardı edilmelerinin yararını ben hâlâ göremiyorum. 16 gibi bir boyut belirlemişsem 20 yok demektir. 20'ye yazmak hata atmalı çünkü hatamı bilmek önemli.

Evet bu aile (Gamsız, Garip ve öteki büyük yığıta Gizem adını taktım) tam anlamıyla yığıt. Çünkü ilgili işlevleri ile (arr, set, push) sahip olduğu diziye, üst üste olacak şekilde bilgileri yazıyor. Ama Garip ve Gamsız'da özel, belki de garip bir durum var. Bunu yığıttan bağımsız özellik diyebiliriz. Bilemiyorum, belki çıkarmalıyım ama istiyorum ki diziyi (yığıtı) boşaltmadan da index yolu ile erişmek.

Kısaca bunlar özel yapıda diziler olarak düşünebiliriz Yığıt gibi ama sorgusuz suhalsiz veri alsın ama dizi boyutu taştığında veya olmayan bir index'e erişildiğinde laga luga etmesin...:)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Avatar
Salih Dinçer #11
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Önemli Not:
Bu yığıtların (dizilerin) hata atmaması (ben buna laga luga diyorum) önemli. Zaten o yüzden varlar. Biz bunları kullandığımız döngü içinde sınırlarını ve davranışlarını bildiğimiz için taşma durumlarını dışarıda tahmin edebiliyoruz. Ama kararlı çalışmaları gerekli yoksa yanlış kullanımda bu sefer gerçekten garip sonuçlar doğurabilir...:)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #12
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ı
Salih Dinçer:
kullandığımız döngü içinde sınırlarını ve davranışlarını bildiğimiz için taşma durumlarını dışarıda tahmin edebiliyoruz.

O zaman hata atmasında da bir sakınca olamaz çünkü zaten hata atılacak davranışta bulunmayacağız diyorsun. ;)

Ama kararlı çalışmaları gerekli yoksa yanlış kullanımda bu sefer gerçekten garip sonuçlar doğurabilir...:)

Asıl garip olan, 'return empty_stack_cell'de ve 'return 0'da olduğu gibi icat edilmiş değerler döndürmektir. İçinde hiç 0 bulunmayan bu türe get() diyorum ve 0 alıyorum. Garip.

(Bu arada, 0 yerine T.init olması gerekiyor. Bir de 'new int' yerine 'new T'.)

Ali
Avatar
Salih Dinçer #13
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Aslında hata atılacak davranışta bulunacağız demek istiyorum. Hata döndüğünde programın işleyişi başka bir yere kaydığı için veri işlemeye ara vermemeli...

Dizi dışındaki adreslere çözüm için ise 0 veya boş bilgi alanından başka bir çarem ne yazık ki yok. Çünkü olmayan bir adrese veri yazılmasını (bire bir eşitlemeyi) ve okunmasını (0 döndürmesini) istiyorum. T.init de olabilir ve hatta dizinin 0. hücresi karavana atış atılan bölüm olabilir...:)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #14
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ı
Salih Dinçer:
hata atılacak davranışta bulunacağız demek istiyorum

Yani programcı yanlışlık yapmış olacak. Bunun hiçbir göstergesi olmayacak. Örneğin, programcı geçersiz olan 10 numaralı yerden veri okuyacak, değeri 0 olarak elde edecek, ve bu yanlışlığı bilmeyecek.

Hata döndüğünde programın işleyişi başka bir yere kaydığı için veri işlemeye ara vermemeli...

O verinin yanlış olduğunda anlaşıyoruz, değil mi? O programın çalıştığı radyasyon tedavi aletinin altında yattığını düşün.

Veri yanlışsa program istenenin dışında davranmaktadır. O alt işlemin hemen durdurulması gerekir. Programın üst düzeyleri bu hatayı yakalayabilirler; programın bileceği iş. İstemezlerse program hemen sonlandırılır ve hatalı davranması hemen önlenmiş olur.

Dizi dışındaki adreslere çözüm için ise 0 veya boş bilgi alanından başka bir çarem ne yazık ki yok. Çünkü olmayan bir adrese veri yazılmasını (bire bir eşitlemeyi) ve okunmasını (0 döndürmesini) istiyorum.

Herhalde oyun gibi önemsiz bir programda bir kullanım amacı vardır. Örneğin, oyundaki bir pencere dışında kalan yerlere de yazılır ama onlar gözardı edilir. (?) Ama o zaman bile pencere büyüse, karakter pencereye yaklaşsa, binanın dışına çıksa, görünmeyen dünyanın bomboş olurdu.

Neyse, senin ihtiyaç duyduğun belli. :)

T.init de olabilir ve hatta dizinin 0. hücresi karavana atış atılan bölüm olabilir...:)

T.init olması gerekiyor yoksa kod yalnızca int ile uyumlu türlerle çalışabiliyor.

Ali
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:
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-21, 19:27:11 (UTC -08:00)