Forum: Ders Arası RSS
Gelin D tadında bir pizza yapalım...:)
pizza Dlang (kuruluş 2012)
Sayfa:  1  2  sonraki 
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ı: Gelin D tadında bir pizza yapalım...:)
Merhaba,

Bugün cuma bereketine, daha önce başladığım bir uygulamayı geliştirmeyi hedefledim. Ancak şirketimiz iflas ediyor! Çünkü gelen siparişleri henüz değerlendiremediği gibi çırağın hazırladığı deneme hamuru fırına verilmeden gaz bitiyor ve fırın soğuyor. Sonuçta çırağa 1 TL maaş kalıyor...:)

siparişVar_mı() işlevi kısmen hazır ama sanırım sistemi tam iyi otutturamadım. Yani çeşitli hileler ile JSON dosyasını değiştirdiğimde uyanmasını ve sipariş föyünü doldurmasını sağlıyorum. Ancak bu işlev o kadar hevesli ki HEDEF = 100'ü tutturmak için başlangıçtaki tüm siparişleri işlemek istiyor. Tabi bir de malzemeDiz() ve fırınaVer() işlevlerine ihtiyacı var. Ayrıca malzemeAl() ve teslimEt() işlevleri de çok önemli. Çünkü:
  • Siparişi teslim edemezsek para kazanamayız,
  • Para kazanmazsak yeni malzeme alamayız,
  • Yeni malzeme alamazsak siparişlere cevap veremeyiz ve

İFLAS: şirketi kapatırız...
/*
    pizzaDlang2.d (06.04.2012)
*/
import std.datetime, std.stdio;
import std.conv, std.file, std.json;
import std.concurrency, core.thread;
 
    immutable _x_HIZ    = 16;   // xKat
 
    immutable SERMAYE   = 1000; // Türk Lirası
    immutable HEDEF     = 100; // adet sipariş
 
    immutable veriDosyası = "pizzaDlang.json";
    immutable kaynakDüğüm = "Siparişler";
 
    JSONValue[] dosyadanJSONdizi(string dosya, string dizi)
    {
        string veriyiOku = to!string(read(dosya));
 
        JSONValue[] JSON = parseJSON(veriyiOku).object[dizi].array;
 
        return JSON;
    }
    enum Hamuru { ince, orta, kalın }
    enum Malzemesi { peynir, sucuk, mantar }
 
    shared struct SİPARİŞLER {
        static size_t numarası;
        static int[HEDEF] bilgileri;
    }
 
    shared struct VARLIKLAR {
        int un, su, yağ, ketçap, peynir, sucuk, mantar;
        int gaz, ısı, kasa = SERMAYE;
 
        this (int m1, int m2, int m3, int m4, int m5, int m6, int m7,
                                        int gaz, int ısı, int masraf) {
            this.un += m1;
            this.su += m2;
            this.yağ += m3;
            this.ketçap += m4;
            this.peynir += m5;
            this.sucuk += m6;
            this.mantar += m7;
            this.gaz += gaz;
            this.ısı += ısı;
            this.kasa -= masraf;
        }
        void Durumu() {
            writef("\tUn %d, Su %d, Yağ %d, Gaz %d, Fırın %d °C\n", un, su, yağ,
                                                                      gaz, ısı);
        }
        void malzemeAl(int xMalzeme) { kasa -= xMalzeme * 10; }
        void teslimEt(int xSipariş) { kasa += 15; }
    }
.
    void hamuraBaşla(Tid durum, shared(VARLIKLAR) * malzeme, Hamuru kalınlık) {
        for(auto i=0;
            i < 10 + kalınlık;
            i++, malzeme.un -= 3, malzeme.su -= 2, malzeme.yağ -= 1)
        {
            if(malzeme.un < 1 || malzeme.su < 1 || malzeme.yağ < 1)
            {
                durum.send(0);
                break;
            }
            durum.send(4)// boşta
            Thread.sleep(dur!"seconds"(i*i/_x_HIZ));
            durum.send(1);
        }
        durum.send(3)// hamur hazır
    }
 
    void fırınıYak(Tid durum, shared(VARLIKLAR) * malzeme, ubyte derece) {
        uint soğumaZamanı = 6;
 
        while (true)
        {
            if(malzeme.gaz > 0 && malzeme.ısı != derece)
            {
                malzeme.gaz--;
                malzeme.ısı++;
            } else durum.send(5);
            Thread.sleep(dur!"msecs"(soğumaZamanı/_x_HIZ));
            durum.send(6)// boşta
            if(++soğumaZamanı % 6 == 0)
            {
                if(malzeme.ısı > 19) malzeme.ısı -= 5; else
                {
                    durum.send(0);
                    break;
                }
            }                    
        }
    }
 
    void siparişVar_mı(Tid durum, shared(VARLIKLAR) * malzeme, int performans) {
        JSONValue[] siparişler;
        size_t siparişNumarası, müşteriNumarası, xSipariş = 0;
        SİPARİŞLER[HEDEF] sipariş;   // auto sipariş = new SİPARİŞLER();
 
        sipariş[xSipariş].numarası = size_t.max;
//* TEST 1
writefln("t-> %d/%d", sipariş[xSipariş].numarası, siparişNumarası); //*/
        do {
            siparişler = dosyadanJSONdizi(veriDosyası, kaynakDüğüm);
 
            siparişNumarası = siparişler[xSipariş + 1].object["nm"].integer;
            müşteriNumarası = siparişler[xSipariş + 1].object["ad"].integer;
//* TEST 2
writefln("t-> %d-%d", sipariş[xSipariş].numarası, siparişNumarası); //*/
            if(sipariş[xSipariş].numarası != siparişNumarası)
            {
                sipariş[++xSipariş].numarası = siparişNumarası;
                sipariş[xSipariş].bilgileri[0] = cast(int)müşteriNumarası;
//* TEST 3
writefln("t-> %d\\%d", sipariş[xSipariş].numarası, siparişNumarası); //*/
                foreach (i, ml; siparişler[xSipariş].object["ml"].array)
                {
                    sipariş[xSipariş].bilgileri[i + 1] = cast(int) ml.integer;
//* TEST 4
writefln("t-> ml[%d][%d]", xSipariş, ml.integer); //*/
                } 
                durum.send(2)// siparişler var
            } else durum.send(1)// sipariş yok
            // Sipariş sorumlusunun ve/veya şirketin performansı:
            Thread.sleep(dur!"msecs"(performans));
            durum.send(1)// sipariş yok
        } while (xSipariş < HEDEF);
        durum.send(0);
    }
 
void main() {
    writeln("pizza Dlang (kuruluş 2012)");
    writeln("==========================");
                                  // un, su, yağ, ketçap, peynir, sucuk, mantar
    shared auto malzeme = VARLIKLAR (100, 100, 100, 100, 100, 100, 100,
                                                         999, 45, 999);
    size_t xDurum = 1;                               // gaz, ısı, kasa (-masraf)
 
    writefln("Şirket %d sermayeyle kuruldu...", SERMAYE);
 
    spawn(&hamuraBaşla, thisTid, &malzeme, Hamuru.kalın);
    spawn(&fırınıYak, thisTid, &malzeme, cast(ubyte)180);
 
    spawn(&siparişVar_mı, thisTid, &malzeme, 100); // ÜZERİNDE ÇALIŞILACAK !!!
 
    writeln("\nVARLIKLAR:");
    while(xDurum)
    {
        xDurum = receiveOnly!size_t();
        if(xDurum == 1) malzeme.Durumu();
        else if(xDurum == 3) writefln("\t\t(HAMUR HAZIR)");
        else if(xDurum == 5) writefln("\t\t(FIRIN HAZIR) Gaz %d, Fırın %d °C",
                                                   malzeme.gaz, malzeme.ısı);       
    }
    writefln("Malzeme bitti !!! (Kasada %d TL var)", malzeme.kasa);
    writefln("İflas ettik...:)");
}/*
    - sipariş[x].bilgileri[0] => xDurum     (durum.send)
    - sipariş[x].bilgileri[1] => xMüşteri   (müşteriNumarası)
    - sipariş[x].bilgileri[2] => Malzemesi.peynir
    - sipariş[x].bilgileri[3] => Malzemesi.sucuk
    - sipariş[x].bilgileri[4] => Malzemesi.mantar
    - Un 67, Su 78, Yağ 89 (kalın hamurlu pizza)
    - Un 70, Su 80, Yağ 90 (orta hamurlu pizza)
    - Un 73, Su 82, Yağ 91 (ince hamurlu pizza)
    - xDurum > 0 (kontrollü çalışmaya devam et)
    - xDurum 1 => sipariş yok
    - xDurum 2 => siparişler var
    - xDurum 3 => hamur hazır
    - xDurum 4 => boşta
    - xDurum 5 => fırın hazır
    - xDurum 6 => boşta
string pizzaDlang.json = ' 
{
    "belge" : "Pizza Siparişleri",
    "türü"  : "ham-UTF8",
    "Siparişler" : [{
            "nm" : 1,
            "ad" : 1,
            "ml" : [90, 100]
        }, {
            "nm" : 2,
            "ad" : 1,
            "ml" : [90, 100]
        }, {
            "nm" : 3,
            "ad" : 2,
            "ml" : [50, 60, 70]
    }]
}';
*/
Başarılar...
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ı
Çok güzel! Canlı bir program olmuş; kendi başına bir şeyler yapıyor! :)

1) Derleme hatası aldım. Benim Makefile'da en son -m32 seçeneği kaldığı içinmiş. 32 bit derlerken size_t 32 bitlik olduğu için ama long her zaman için 64 bitlik olduğu için şurada türler uyuşmadı:
            siparişNumarası = siparişler[xSipariş + 1].object["nm"].integer;
            müşteriNumarası = siparişler[xSipariş + 1].object["ad"].integer;
Sağ tarafların başlarına cast(size_t) yazınca derlendi.

Tabii buradaki sorun json'un .integer'ının long olmasından kaynaklanıyor. Daha küçük bir türe dönüştürürken cast kullanmanın gerekmesi doğal. size_t'nin sorunu tam büyüklüğünün derlendiği zamanki ortama bağlı olması ve long'dan daha küçük olabilmesi.

Bunları yazarken bir programcı olarak suçluluk da duyuyorum :) ama size_t olmazsa da olmuyor. C'de ve C++'ta da bulunan sorunlar bunlar...

2) -m32 ile yukarıdaki gibi derledikten sonra program bir hata ile sonlandı:

std.concurrency.MessageMismatch@std/concurrency.d(235): Unexpected message type

Onun da nedeni size_t. receiveOnly yalnızca size_t istiyor ama bütün send()'ler int gönderiyorlar. int alınca çalıştı:
        xDurum = receiveOnly!int();
Daha genel olarak int alan bir temsilci ve receive() ile de çalışır:
        // xDurum = receiveOnly!size_t();
        receive((int durum) { xDurum = durum; });
Ve denedim; 64 bitte de doğru çalışıyor.

3) Bundan sonrakiler kodla ilgili çoğu önemsiz gözlemler. Hatta kodun eksik olduğunu da söylemiştin zaten... Buna rağmen... :)

  • Ben SERMAYE ve veriDosyası gibi derleme zamanında bilinen sabitleri artık enum olarak tanımlıyorum. Bu konudaki fikirlerim çok yakın zaman önce oturdu.

  • Kod genelde gerçekten çok hoş. :) Hamuru ve Malzemesi özellikle noktayla kullanılırken çok okunaklı oluyorlar. Tabii şu açıklamalar da enum olmak için can atıyorlar: ;)
/*    - xDurum > 0 (kontrollü çalışmaya devam et)
    - xDurum 1 => sipariş yok
    - xDurum 2 => siparişler var
    - xDurum 3 => hamur hazır
    - xDurum 4 => boşta
    - xDurum 5 => fırın hazır
    - xDurum 6 => boşta
*/

  • SİPARİŞLER yapısının ismi sanki SİPARİŞ olmalı ve 'sipariş' değişkeninin ismi sanki 'siparişler' olmalı. Belki de onu tam olarak anlamadığım için öyle düşünüyorumdur. HEDEF adetlik sipariş var ama her birisinin içinde de HEDEF adet bilgi var. Neden HEDEF çarpı HEDEF bilgi gerektiğini anlamadım.

  • VARLIKLAR'ın üyelerinden bazıları birbirleriyle yakından ilgili görünmüyorlar. Bölmek daha yararlı olabilir.

  • Bu tamamen kişisel tercih ama ben post-increment ve post-decrement yazımlarına hiç alışamayacağım: :)
                malzeme.ısı++;
Benim tercihim:
                ++malzeme.ısı;

  • Salih'in kısa kod yazmayı sevdiğini biliyorum ama bu tam bir tuzak olmuş: :)
                if(malzeme.ısı > 19) malzeme.ısı -= 5; else
                {
                    durum.send(0);
                    break;
                }

else farkedilene kadar bloğun if koşuluna bağlı olduğu sanılıyor. else'i görmemek zor belki ama yine de else'in nerede olduğuna bağlı olarak beynimizde sürekli anlam kaydırmaları gerekiyor. Çünkü hemen üstünde de şu var:
            if(malzeme.gaz > 0 && malzeme.ısı != derece)
            {
                malzeme.gaz--;
                malzeme.ısı++;
            } else durum.send(5);
Oraki kod bloğu ise if'in koşuluna bağlı!

Kodun tutarlılığı çok önemli. Çalıştığım yerde biz aşağıdaki yazım standardını kullanıyoruz:
            if(malzeme.gaz > 0 && malzeme.ısı != derece) {
                --malzeme.gaz;
                ++malzeme.ısı;
 
            } else {
                durum.send(5);
            }
// ...
                if(malzeme.ısı > 19) {
                    malzeme.ısı -= 5;
 
                } else {
                    durum.send(0);
                    break;
                }

  • Daha geliştireceğini söylüyorsun zaten ama mesajlaşma konusunda aklıma gelenler şöyle:

shared ile veri paylaşımı yerine ana iş parçacıklarından sipariş göndermek daha temiz ve güvenli olur. Örneğin bir iş parçacığı yalnızca sipariş alır ve ana iş parçacığına bir Sipariş nesnesi gönderir. Ana iş parçacığı da malzemeler arasından o siparişe uygun miktarda malzeme çıkartır ve bu malzemeleri ve siparişi bir aşçı iş parçacığına verir. Aşçı iş parçacığı da tekrar ana iş parçacığına bir Pizza nesnesi gönderir. Sonra ana iş parçacığı garson iş parçacığına bu pizzayı verir. vs. Yani iş parçacıklarının içine ortaklaşa bakıp değiştirdikleri bir dizi yoktur ve gereken bilgi ana iş parçacığından dağıtılır.

hamuraBaşla() ve fırınıYak() gibi iş parçacıklarının sorunları, aynı 'malzeme' nesnesi üzerinde değişiklik yapıyor olmaları. Eninde sonunda okuma ve yazma karışıklıkları oluşacaktır.

Örneğin değeri 42 olan aynı değişkeni birisi ++i yapacak diğeri --i yapacak. Sonuçta değerin 42 olarak kalması gerekirken hangisi son yazmışsa onun değeri kalır. Yani ikisi de 42 olarak okurlar, birisi 43 değerini üretir diğeri de 41. Sonra i'ye yeni değerini yazarken son yazan kazanır ve i ya 43 olur ya da 41.

Bu gibi sorunların önüne geçmek için synchronized anahtar sözcüğü kullanılabiliyor ama yine de çok belalıdır. O yüzden en iyisi 'malzeme'de yaşanan veri paylaşımını önlemektir.

  • Sanıyorum nm "numara" ve ml de "malzeme listesi" anlamına geliyor. (?) ;)

Ama dediğim gibi, program benim çok hoşuma gitti.

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ı
Yorumların için  teşekkürler; doğru tahminlerin içinde...:)

Korkarım, dillerdeki "type inference" özellikleri zamanla gelişse de senin doğru tahminlerin kadar hiç bir zaman olamayacaklar. Biz programcıları hep bu türler kısıtlamakta. Ne integer gerçekten tam sayılar kümesini temsil ediyor ne de long veya cent gibi değişkenler yeterince büyükler. Gün gelecek bütün bunlar da yetmeyecek. Bence hala programlama dilleri çok aptal. Her biri akıllı olmak için karmaşıklaşıyorlar ama sonra dilin tüm özelliklerini kullanan senin gibi akıllı insanlar azalınca bir çok şeyi emekliye ayırıyorlar veya Scala gibi yeni bir dil ortaya çıkıyor.

Örneğin Scala'da public ve default dışında çok fazla kafa karıştırıcı keyword yok. Süslü parantezler ve satır sonu noktalı virgüller bile yok. Oysa bugün dillere bakıyorumda aklıma geldiği sırayla yazarsak static, shared, protected, private, public vb. bir sürü şeyle dolu...:)

acehreli:
...Yani iş parçacıklarının içine ortaklaşa bakıp değiştirdikleri bir dizi yoktur ve gereken bilgi ana iş parçacığından dağıtılır.

hamuraBaşla() ve fırınıYak() gibi iş parçacıklarının sorunları, aynı 'malzeme' nesnesi üzerinde değişiklik yapıyor olmaları. Eninde sonunda okuma ve yazma karışıklıkları oluşacaktır.

Örneğin değeri 42 olan aynı değişkeni birisi ++i yapacak diğeri --i yapacak. Sonuçta değerin 42 olarak kalması gerekirken hangisi son yazmışsa onun değeri kalır. Yani ikisi de 42 olarak okurlar, birisi 43 değerini üretir diğeri de 41. Sonra i'ye yeni değerini yazarken son yazan kazanır ve i ya 43 olur ya da 41.

Bu gibi sorunların önüne geçmek için synchronized anahtar sözcüğü kullanılabiliyor ama yine de çok belalıdır. O yüzden en iyisi 'malzeme'de yaşanan veri paylaşımını önlemektir.
Senin söylediğin daha mantıklı görünüyor. Çünkü ileride aynı anda pizzalar pişecek ve belki siparişleri yetiştirebilmek için bir hamurcu ustası daha alacağız...:)

Gerçi her şey eksiye gidiyor ve malzeme deposu ortak ve yeni ürün alımı yapılmadığında herşey eksiliyor. Isının yükseldiği de tek bir fırın var. Belki de sorun olmaz bilemiyorum. Aslında bu kadar çok ayrıntıya kafa yormak da istemiyorum. Benim tek istediğim şu:

Teknik açıdan iki program (master ve slave) olacak ve master siparişi veren ayrı bir derlenmiş D programcığı olacak. Bu iki program JSON ile haberleşecek. Aslında socket programming yapabilsek daha leziz olur ya. Neyse slave ise siparişi alacak ve pizza üretecek. Üretirken de kendi için bağımsız programları (thread) oluşturucak. Onlar kendi başlarına özgürce (malzeme bitmedikçe) harıl harıl çalışacaklar. Sanırım hedefe çok uzak değiliz, ne dersiniz?

Sevgiler, saygılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Avatar
Salih Dinçer #4
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #2
acehreli:
Derleme hatası aldım. Benim Makefile'da en son -m32 seçeneği kaldığı içinmiş.
Doğrudur, bir süredir 64 bit sistem üzerinde çalıştığım için -m32'yi ihmal ediyorum. Aslında artık size_t kullanmayacağım. Kendisine çok cins olmaya başladım...:)

acehreli:
Ben SERMAYE ve veriDosyası gibi derleme zamanında bilinen sabitleri artık enum olarak tanımlıyorum. Bu konudaki fikirlerim çok yakın zaman önce oturdu.
Vallahi immutable olayını kafama sokan kim acaba...:)

Onu oradan çıkartıp senelerin enum'u sokmak zor olacak ama deneyeceğim. Gerçi sıklıkla enum kümeleri kullanıyorum. Kelimeleri güzel seçince gerçeken hoş duruyorlar. Bir de if() kullanmadan enum'ları ters kullanabilsek; acaba mümkün mü? Yani ben 1 değerini verdiğimde kümedeki ikinci değeri, yani 1'e eşitlenmiş enum'u verse. Bir de D'de önemli bir eksilik var ki o da INC (veya DEC) enum yaptığımızda (örn. cast(Hamuru) hamur++) eğer son elemandaysa baştaki elemanın değerini döndüreceğine, son elemanın değerini bir arttırıyor. Belki de ben yanlış bir şey yapıyorumdur.

Not: enum adaylarına bir çeki düzen vereceğim...

acehreli:
Salih'in kısa kod yazmayı sevdiğini biliyorum ama bu tam bir tuzak olmuş: :)
                if(malzeme.ısı > 19) malzeme.ısı -= 5; else
                {
                    durum.send(0);
                    break;
                }
Hem de nasıl...:)

N'apayım, kodu ben yazdığım için gözümden iki satırı silmek hoşuma gidiyor ve else'ye yoğunlaşıyorum. Çünkü biliyorum ki orası ısıyı düşürüyor ve önemli olan oda sıcaklığına eriştiğimizde ne yapmam gerektiği. Yani kendimce bir scope yapıyorum. Hadi buna "private scope" diyelim...:)

acehreli:
SİPARİŞLER yapısının ismi sanki SİPARİŞ olmalı ve 'sipariş' değişkeninin ismi sanki 'siparişler' olmalı. Belki de onu tam olarak anlamadığım için öyle düşünüyorumdur. HEDEF adetlik sipariş var ama her birisinin içinde de HEDEF adet bilgi var. Neden HEDEF çarpı HEDEF bilgi gerektiğini anlamadım.
SİPARİŞLER yapısının ikinci elemanını dinamik dizi yapmak istedim. Ama henüz bu kavrama alışık olmadığım için static'e çevirdim. Ancak anlayamadığım bir sebepten (gerçi bunu dersinde belirtmişsin; sonradan okudum) dinamik diziye JSON'dan gelen verileri yerleştiremiyordum. Hiç bir run-time error vermediği gibi orası yokmuş gibi çalışmaya devam ediyordu. Mecburen böyle bir şey yapıp sonra bakmak üzere geçici bir çözüm ürettim.
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Bu mesaj 2 defa değişti; son değiştiren: Salih Dinçer; zaman: 2012-04-06, 14:45.
acehreli (Moderatör) #5
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 #3
Salih Dinçer:
Ne integer gerçekten tam sayılar kümesini temsil ediyor ne de long veya cent gibi değişkenler yeterince büyükler. Gün gelecek bütün bunlar da yetmeyecek.

Tamsayıları sonsuz basamaktan oluşan diller var. D'de de BigInt kullanılabilir. Sorun, o türlerin mikro işlemci tarafından desteklenmemeleri. Hız önemli olmadığı sürece bir BigInt türü kullanılabilir. Hatta başka dil de kullanılabilir. ;)

Gerçi her şey eksiye gidiyor ve malzeme deposu ortak ve yeni ürün alımı yapılmadığında herşey eksiliyor. Isının yükseldiği de tek bir fırın var. Belki de sorun olmaz bilemiyorum.

Şu anda sorun olmayabilir. Ama eksiye götüren iki iş parçacığı olduğu zaman bu sefer de 42'nin 40'a inmesi gerekirken 41'e inmiş olabilir.

Veri paylaşımına dayalaı geleneksel multithreaded programlama vebalıymış gibi kaçınılması gereken bir yöntemdir. Ortaya çıkan sorunların sonu gelmez. Veri paylaşımının bazı sorunlarını şurada "Veri paylaşmak için shared" başlığı altında göstermişim:

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

Orada bir de BankaHesabı örneğine bakmanızı öneririm. O da programın takılıp kaldığı çok tanıdık bir örneği gösterir.

Teknik açıdan iki program (master ve slave) olacak ve master siparişi veren ayrı bir derlenmiş D programcığı olacak. Bu iki program JSON ile haberleşecek.

O güzel çünkü farklı programlar olunca aynı verinin paylaşımı söz konusu olmuyor. Veri ancak mesajlaşarak iletiliyor.

Ali
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ı
Yanıtlanan mesaj #4
Salih Dinçer:
Onu oradan çıkartıp senelerin enum'u sokmak zor olacak ama deneyeceğim.

enum olarak tanımlanan değişkenler senelerin enum'undan farklılar çünkü bir enum türu oluşturmuyorlar. Bu kullanımda enum, C'de #define ile tanımlanan sabit değerlerin yerine geçiyor:

    enum SERMAYE   = 1000;

Yani ben 1 değerini verdiğimde kümedeki ikinci değeri, yani 1'e eşitlenmiş enum'u verse.

O fazla tehlikeli kabul ediliyor. Her tamsayının enum değer karşılığı bulunmadığı için çalışma zamanında arama yapılması gerekir. Çözümü tür dönüşümü:

    enum E { yazı, tura }
    E e = cast(E)1;
    assert(e == E.tura);

Bir de D'de önemli bir eksilik var ki o da INC (veya DEC) enum yaptığımızda (örn. cast(Hamuru) hamur++) eğer son elemandaysa baştaki elemanın değerini döndüreceğine, son elemanın değerini bir arttırıyor.

Onu bazen ben de istiyorum ama herhalde fazla özel bir davranış olarak görüyor olmalılar. Ne yazık ki kendimiz yapmak zorundayız. Bazen şu yeterli oluyor:

    import std.traits;
 
    foreach (e; (__traits(allMembers, E))) {
        writeln(e);
    }

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ı
Merhaba,

Henüz pizza yapmayı öğrenmeden (Master<->JSON<->Slave iletişimine geçmeden) ekmek yapmayı (thread'ların davranışlarını) tam anlamıyla öğrenmem gerektiğini düşünüyorum! Belki daha 10 fırın ekmek yemek gerekiyor ki bu fırında henüz tek fırın var olduğu düşünülürse sanırım çok hızlı gitme hevesliliğini dizginlemeliyim...:)

Bu sabah aşağıdaki (test yazılımı ekmekYap.d) ile koşmadan (şirketi açmadan) evvel emeklemeyi öğrenebileceğimi düşündüm. Her ekmek yapıldığında yeni bir thread oluşturmakla yetinmeyip patronu kovdum ve müdür olarak bir süreliğine fırıncıyı atadım. O da 5 ekmeğe kadar dayanabildi ve kendi işine bakmaya devam* etti.
* (Fırın soğumadan şirket kapanıyor; yani thread_fırınYak düzgün çalışmıyor ki bunu harcadığı gazın azlığından anlıyorum...)
import std.concurrency, core.thread, std.stdio;
 
    immutable int _x_HIZ = 32;              // xKat
    immutable int milyar = 1_000_000_000;   // 1 milyar birim
 
    enum Hamuru {
                    ince = 1,
                    orta,
                    kalın 
    } /* Bir ekmek için =>
        - Un 120, Su 60, Yağ 90, Tuz 30 (kalın hamurlusunda)
        - Un 80, Su 40, Yağ 60, Tuz 20 (orta hamurlusunda)
        - Un 40, Su 20, Yağ 30, Tuz 10 (ince hamurlusunda)
    */
    shared struct Kiler {
        int un, su, yağ, tuz, ekmek;
 
        this (int _un, int _su, int _yağ, int _tuz)
        {
            this.un += _un;
            this.su += _su;
            this.yağ += _yağ;
            this.tuz += _tuz;
        }
 
        void Durumu() {
            writefln("[Un %d, Su %d, Yağ %d, Tuz %d]: Ekmek = %d",
                                          un, su, yağ, tuz, ekmek);
        }
    }
    
    shared struct Fırın {
        int gaz;
        ubyte ısı;
 
        this (ulong _gaz, ubyte _ısı)
        {
            this.gaz += _gaz;
            this.ısı += _ısı;
        }
    }
 
    void ekmekYap(Tid durum, shared(Kiler) * kiler, Hamuru kalınlık)
    {
        for(auto i = 0;
            i < 10 * kalınlık;
            i++, kiler.un -= 4, kiler.su -= 2,
                 kiler.yağ -= 3, kiler.tuz -= 1)
        {
            if(kiler.un < 1 || kiler.su < 1 ||
               kiler.yağ < 1 || kiler.tuz < 1)
            {
                durum.send(0);
                break;
            }
            durum.send(2)// bekleme
            Thread.sleep(dur!"seconds"(i*i/_x_HIZ));
            durum.send(1)// yazdırma
        }
        durum.send(3);
    }
 
    void fırınYak(Tid durum, shared(Fırın) * fırın, ubyte derece) {
        uint soğumaZamanı = 6;
 
        while (true)
        {
            if(fırın.gaz > 0 && fırın.ısı != derece)
            {
                fırın.gaz--;
                fırın.ısı++;
            } else {
                durum.send(5);
            }
            Thread.sleep(dur!"msecs"(soğumaZamanı/_x_HIZ));
            durum.send(4);
            if(++soğumaZamanı % 6 == 0)
            {
                if(fırın.ısı > 19)
                {
                    fırın.ısı -= 5;
                } else {
                    durum.send(0);
                    writeln("Fırın oda sıcaklığına geldiği için şirketi kapa!");
                    writefln("- Harcanan Gaz: %d", milyar - fırın.gaz);
                    break;
 
                }
            }                    
        }
    }
 
void main() {
    shared auto kiler = Kiler(milyar, milyar, milyar, milyar);
    shared auto fırın = Fırın(milyar, 19);
 
    Hamuru ekmekÇeşiti = Hamuru.ince;
 
    spawn(&ekmekYap, thisTid, &kiler, ekmekÇeşiti);
    spawn(&fırınYak, thisTid, &fırın, cast(ubyte)250);
 
    int xDurum = 1;
    while(xDurum)
    {
        xDurum = receiveOnly!int();
        if(xDurum == 1) kiler.Durumu();
        else if(xDurum == 3)
        {
            kiler.ekmek++;
            if (kiler.ekmek == 100) break;
            spawn(&ekmekYap, thisTid, &kiler, ekmekÇeşiti);
        }
        else if(xDurum == 5 && kiler.ekmek < 5)
        {
            writefln("\t(FIRIN HAZIR) Gaz %d, Fırın %d °C",
                                      fırın.gaz, fırın.ısı);
            spawn(&ekmekYap, thisTid, &kiler, ekmekÇeşiti);       
        }
    }
    writefln("- Harcanan Tuz: %d", milyar - kiler.tuz);
    writefln("Hala fırın çalışıyor ve şu an %d °C sıcaklığında;", fırın.ısı);
}/*
[Un 999959784, Su 999979892, Yağ 999969838, Tuz 999989946]: Ekmek = 999
- Harcanan Tuz: 10054
[Un 999995820, Su 999997910, Yağ 999996865, Tuz 999998955]: Ekmek = 99
- Harcanan Tuz: 1045
[Un 999999428, Su 999999714, Yağ 999999571, Tuz 999999857]: Ekmek = 9
- Harcanan Tuz: 143
*/
Komiktir, aşağıdaki dip notu "uyuşmayabilir!" diye bitirmişim. Sanırım değerler de uyuşmuyor ve ben henüz bunun neden olduğunu anlayamadım. Belki Ali hocamın işaret ettiği gibi baş belası bir durum. Aslında fırıncı patronluk vazifesini yaparken (5 ekmek üretilene kadar) sanırım 30-40 thread oluşturuyor olmalı. Yani 100 ekmek için 1000 değil de 1045 birim tuz harcamasına etkisinin olmaması gerekiyordu. Ama her seferinde yaklaşık %5'lik hata payı var ki değer olarak hep aynı. Zamanı hızlandırma (tam sayıları böldüğümüzde küsüratlı bir şekilde ifade edememiz) ile alakalı olabilir. Çünkü hız değişliklerinde değerlerde aşırı sapma var!

Bir de programın gerçekten sonlanması (konsola düşmesi) azıcık daha vakit alıyor! Tamam fırın en geç sonlanan olduğu için böyle ama neden ekrana yazılan değerler (log'lar) düştüğünde işlevden kuramsal açıdan çıkmasın! Bir şeyler karışıyor ama dediğim gibi henüz anlayamadım. Gerçi anladığım bir şey var o da patronluğu fırıncı devir aldığında (durum == 5'de thread kurulduğunda) program sonlanmasına kadar ekmeler 7'şer 7'şer üretilmekte ama 64x'de patron dükkandan çıkamadığı için değerlerde sapıtma yok ki karmaşık durumlarda, thread'lerin kimliklerini (Tid) ve terminate durumlarını sorgulamamız anlamına gelebilir.

Dip Not: Çok fazla beklemekten sıkılırsanız 64x hoşunuza gidebilir. Ama siz yine de bunu kullanmayın çünkü hata payını gerçekçi hesaplayabilmek için kiler.ekmek < 5 yaptığımdan test sonuçlarımız uyuşmayabilir.

Başarılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
zafer #8
Üye Tem 2009 tarihinden beri · 700 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Bu tür örnekleri seviyorum. Hem sorunu daha somut hale getiriyor hemde öğrenmesi ve kavramlar üzerinde tartışılması daha kolay olur. Ayrıca benimde çok hoşuma gitti. Cuma günü ilk gördüğümde ilgilenmek istedim ama yoğunluktan bu güne kaldı. En son eklediğin kod üzerinde biraz inceleme yaptım. main için aklıma takılanlar şunlar;

void main() 
{
    shared auto kiler = Kiler(milyar, milyar, milyar, milyar);
    shared auto fırın = Fırın(milyar, 19);
 
    Hamuru ekmekÇeşiti = Hamuru.ince;
 
    // Fırın daha hazır olmadan ekmek yaomak doğrumu? Ayrıca biz piza yapmıyor muyuz?
    spawn(&ekmekYap, thisTid, &kiler, ekmekÇeşiti);
 
    spawn(&fırınYak, thisTid, &fırın, cast(ubyte)250);
 
    int xDurum = 1;
    while(xDurum)
    {
        xDurum = receiveOnly!int();
 
        if(xDurum == 1) 
        {
            kiler.Durumu();
        }
        else if(xDurum == 3)
        {
            kiler.ekmek++;
            if (kiler.ekmek == 100) break;
            spawn(&ekmekYap, thisTid, &kiler, ekmekÇeşiti);
        }
        else if(xDurum == 5 && kiler.ekmek < 5)
        {
            writefln("\t(FIRIN HAZIR) Gaz %d, Fırın %d °C",
                                      fırın.gaz, fırın.ısı);
            spawn(&ekmekYap, thisTid, &kiler, ekmekÇeşiti);       
        }
 
        // Fırın durumunu kontrol etmek için fırının hazır durumu geldiği anda programı durdurdum.
        if (xDurum == 5 || xDurum == 0)
        {
            writefln("xDurum : %s", xDurum);
            system("PAUSE");
        }
    }

Burada döngüden önce yani fırın yanmadan önce ekmek olayına başlamışız bu doğru mu?

Ayrıca döngü içinde fırın hazır olduğunda ekranda 4 tane ekmek (piza) hazırlanmıştı sanırım burada da bir sorun var yada olayı ben tam anlamadım?
https://github.com/zafer06 - depo
Avatar
Salih Dinçer #9
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #7
Az önce, şuradaki tartışmayı (std.concurrency'nin doğuşunu) okudum ve ilk sürümlerden biri olan aşağıdaki sınıfı denemeye çalıştım:

http://www.invisibleduck.org/sean/src/concurrency.d

Tabi başarılı olamadım çünkü eski bir sınıf olduğu için bir çok yerine el atılması gerekiyordu. Ben de D2'nin hangi sürümüyle uyumlu olduğunu öğrenmeye çalıştım. Aslında teorik olarak 2.048 (8 Ağustos 2010) sürümünde çalışması gerekiyordu ama şu hatayı verdi:
std\format.d(306): Error: cannot implicitly convert expression (& arg) of type shared(int)* to const(void)*
Ayrıca 2.051 sürümü de dahil arada kalanlarda ise "dur!: duration" hatası veriyor ki yazdığım programın (ekmekYap.d) core.thread ile de uyumsuzluğu anlamına geliyordu. Bende denemelerimi son bir seneki sürümler üzerine odakladım...:)

İlk dikkatimi çeken derleme boyutlarındaki ciddi farklılık oldu. Aslında bu doğal olabilir çünkü geçen sene DMD linker ve runtime kısmında epey bir düzeltme yapmış. Ama 2.056 sürümünde 4 kata çıkması çok ilginç görünüyor. Ben de diğer dış kaynaklı hatalardan emin olmak için testi daha kararlı olduğunu düşündüğümden* lisanslı Windows7-32 bit platformunda yaptım. Özetle aşağıda göreceğiniz üzere binary dosyaların çoğu şu an ki sürüme göre en az iki kat daha büyük boyutlarda oluşuyor. Asıl önemli olan da, yeni DMD sürümlerine göre daha kararlı çalışmalarıydı, hatta yukarıdaki kod tam da istediğim gibi çoğunda doğru çalıştı:

- Harcanan Tuz: 1000 (2.052)
09.04.2012  15:10           541.212 ekmekYap.exe

- Harcanan Tuz: 1000 (2.053)
09.04.2012  15:19           536.604 ekmekYap.exe

- Harcanan Tuz: 1000 (2.054)
09.04.2012  15:30           507.420 ekmekYap.exe

- Harcanan Tuz: 1000 (2.055)
09.04.2012  15:42           549.916 ekmekYap.exe

- Harcanan Tuz: 1000 (2.056)
09.04.2012  15:51         1.108.508 ekmekYap.exe

- Harcanan Tuz: 1024 (2.057)
09.04.2012  15:57           890.908 ekmekYap.exe

- Harcanan Tuz: 1015 (2.058)
09.04.2012  16:01           255.004 ekmekYap.exe

Yukarıdaki son iki test raporundan anlaşılacağı üzere program 2.057'den itibaren doğru çalışmıyor. Çünkü 100 ekmek için 1000 birim tuz harcanması gerekiyordu. Bunun olası cevabı fırınYak() işlevinin devreye girememesi olabilir. Ama buna neyin sebep olduğunu bilmiyorum. Tek bildiğim son sürümde de programın göçtüğü...:)

* Son sürümde program sonlanması gerekirken, Windows göçme raporu verdi:

Sorun imzası:
  Sorunlu Olay Adı:    APPCRASH
  Uygulama Adı:    ekmekYap.exe
  Uygulama Sürümü:    0.0.0.0
  Uygulama Zaman Damgası:    00000000
  Hata Modülü Adı:    StackHash_e98d
  Hata Modülü Sürümü:    0.0.0.0
  Hata Modülü Zaman Damgası:    00000000
  Özel Durum Kodu:    c0000005
  Özel Durum Uzaklığı:    01330d05
  OS Sürümü:    6.1.7601.2.1.0.256.48
  Yerel Kimlik:    1055
  Ek Bilgiler 1:    e98d
  Ek Bilgiler 2:    e98dfca8bcf81bc1740adb135579ad53
  Ek Bilgiler 3:    6eab
  Ek Bilgiler 4:    6eabdd9e0dc94904be3b39a1c0583635
Sevgiler, saygılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Bu mesaj Salih Dinçer tarafından değiştirildi; zaman: 2012-04-09, 07:04.
zafer #10
Üye Tem 2009 tarihinden beri · 700 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Salih Dinçer:
Tek bildiğim son sürümde de programın göçtüğü...:)

[Un 999995848, Su 999997924, Ya─ş 999996886, Tuz 999998962]: Ekmek = 98
[Un 999995824, Su 999997912, Ya─ş 999996868, Tuz 999998956]: Ekmek = 99
[Un 999995824, Su 999997912, Ya─ş 999996868, Tuz 999998956]: Ekmek = 99
[Un 999995824, Su 999997912, Ya─ş 999996868, Tuz 999998956]: Ekmek = 99
[Un 999995824, Su 999997912, Ya─ş 999996868, Tuz 999998956]: Ekmek = 99
[Un 999995824, Su 999997912, Ya─ş 999996868, Tuz 999998956]: Ekmek = 99
[Un 999995824, Su 999997912, Ya─ş 999996868, Tuz 999998956]: Ekmek = 99
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
[Un 999995820, Su 999997910, Ya─ş 999996865, Tuz 999998955]: Ekmek = 99
- Harcanan Tuz: 1045
Hala firin calisiyor ve su an 248 ┬░C sicakliginda;
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
-> firinYak while donuyor...
PS E:\Proje - D\SubLime_Test>

Salih, acaba program sonlanmasına rağmen fırınYak() için başlattığın kanalın (thread) halen devam ediyor olması programın çökmesine sebep olabilir mi?

Ayrıca main() içinde ekmekYap() için iki ayrı kanal başlatıyorsun birisi döngüden önce, o kanal ne için gerekiyor? Belki programın tutarsız değerler üretmesine neden olan o kanaldır. Ayrıca daha fırın hazır değilken ekmekYap() çağrılması doğru mu?

Bu arada bence fırını projenin içinden çıkarıp ayırabilsek hem kodlama daha rahat olur hemde sistem daha esnek olur bence. Bu sebeple basit bir sınıf hazırlamaya çalıştım, amacım fırını kendi başına ayrı bir modül kullanarak girdi vermek ve çıktı almak dışında programdan uzakta tutmak, her türlü eleştiriyi bekliyorum, eksikleri çok biliyorum :)

class Firin 
{
    private int _firinSicakligi = 0;     // 0..300 birim sıcaklık arasında çalışır.
    private int _mevcutGazMiktari = 0;   // 0...10_000 birim gaz depolanabilir.
 
    public this(int mevcutGazMiktari, int firinSicakligi) 
    {
        _mevcutGazMiktari = mevcutGazMiktari;
        _firinSicakligi = firinSicakligi;
    }
 
    private void FiriniYakSicakligiAyarla()
    {
        // Her 100 birim sıcaklık için 5 birim gaz yakar.
        int harcananGazMiktari = _firinSicakligi * 5;
 
        assert(_mevcutGazMiktari < harcananGazMiktari, "Firindaki gaz miktari yetersiz.");
    }
 
    public void PizayiFirinaVer(Hamuru pizaHamuru)
    {
        assert(_firinSicakligi == 0, "Firin sicakligi yetersiz.");
 
        if (_firinSicakligi > 0)
        {
            switch (pizaHamuru)
            {
                case Hamuru.ince :
                    mevcutGazMiktari -= 2// Pişirme için harcanan gaz
                    break;
 
                case Hamuru.orta :
                    mevcutGazMiktari -= 3;
                    break;
 
                case Hamuru.kalın :
                    mevcutGazMiktari -= 4;
                    break;
            }
 
            //Piza pişiyor..
            Thread.sleep!("seconds")(10);   // 10 saniye bekle
        }
    }
}
https://github.com/zafer06 - depo
Avatar
Salih Dinçer #11
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Merhaba Zafer,

Senin yazdığın şekilde de denemeye başladım. Sonuçları 24 saat sonra ifade edeceğim inşaallah...

Aslında bu kodun A ile B veya S ile Z şeklinde yazılmasının hiç bir mahsuru yok. Hatta fırın da yanmayabilir ki önemli olan başta, ortada ve belki sonda karışık bir şekilde (rasgele değil ranfomize fonksiyonundan şimdilik uzak durmalı) herhangi bir işlevin Thread özelliğiyle çağrılması bize bu yapının inceliklerini daha iyi anlamamızı sağlayabilir. Ama spawn() yoluyla, yani dolaylı bir şekilde Thread'ların oluşturulması beni rahatsız ediyor. Henüz std.concurrency.d* alternatifini yapamam da beni iki kat rahatsız etmekte...:)

* (sdb dizininde concurrency.d dosyası oluşturdum ve sdb.concurrency şeklinde import ediyorum...)

Bence D'nin kesinlikle Sean'nın yazdığı şu sınıftan** daha iyi bir şeye ihtiyacı var ya da bu sınıfı kullanmadan Thread'ları birbirleriyle haberleştirmeyi öğrenmeye benim ihtiyacım var. Ama ben bunların nasıl yapılacağını henüz bilmiyorum...:(

** (aslında şu mailbox mantığı gayet güzel, bir de sınıfı import etmeden doğrudan denemeli)

Başarılar...
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ı
Hiç derinlemesine düşünmedim ve kodu derlemedim bile ama böyle farklı bir tür tanımlamak tabii ki yararlı olur.

Hızlıca gördüklerim şöyle:

1) _firinSicakligi ve _mevcutGazMiktari üzerinde açıklamalarda belirtildiği gibi kesin kısıtlamalar varsa bunları D olanakları ile de garantileyebiliriz. invariant blokları sınıfın mutlak değişmezliğini garanti eder:

class Firin
{
// ...
    invariant()
    {
        assert((_firinSicakligi >= 0) && (_firinSicakligi < 300));
        assert((_mevcutGazMiktari >= 0) && (_mevcutGazMiktari < 10_000));
    }
}

invariant blokları sözleşmeli programlamanın bir parçasıdır ve şu zamanlarda çağrılırlar:

  • Kurucu işlev sonunda; böylece nesnenin yaşamına tutarlı olarak başladığı garanti edilir.

  • Sonlandırıcı işlev çağrılmadan önce; böylece sonlandırma işlemlerinin tutarlı üyeler üzerinde yapılacakları garanti edilir.

  • public bir işlevden önce ve sonra; böylece üye işlevlerdeki kodların nesneyi bozmadıkları garanti edilir.

Böylece Firin nesnelerinin tutarsız durumda bulunmaları önlenmiş olur.

Ancak, program -release seçeneği ile derlendiğinde bütün assert'ler gibi bütün sözleşmeli programlama olanakları da (in, out, ve invariant blokları) koddan çıkartılırlar. (Bunun istisna: assert(false)'lar hiçbir zaman koddan çıkartılmazlar.)

Bu da bizi assert'lerde de yaşadığımız güç bir kararla karşı karşıya bırakır: Ne zaman assert kullanmalı, ne zaman hata atmalı? Bu soruyu yanıtlamak için sorulması gereken soru şu: nesne hiç kullanıcılar tarafından oluşturuluyor mu, yoksa yalnızca bu kütüphanenin veya programın kodları tarafından mı oluşturuluyor?

Eğer Firin türünü modülün arayüzüne koymuşsak, kullanıcıların Firin nesnelerini yanlış değerlerle oluşturmalarını invariant blokları ile engelleyemeyiz çünkü -release ile derlediklerinde invariant denetimleri yok olurlar.

O yüzden, eğer Firin modülün arayüzündeyse, ya invariant bloğunun yerine ya da onunla birlikte ayrıca kurucuya enforce() denetimleri de eklenmelidir:

import std.exception;
class Firin 
{
// ...
    public this(int mevcutGazMiktari, int firinSicakligi) 
    {
        enforce((firinSicakligi >= 0) && (firinSicakligi < 300),
                "Geçersiz fırın sıcaklığı");
        enforce((mevcutGazMiktari >= 0) && (mevcutGazMiktari < 10_000),
                "Geçersiz gaz miktarı");
 
        _mevcutGazMiktari = mevcutGazMiktari;
        _firinSicakligi = firinSicakligi;
    }

Tabii hata mesajı örneğin format() ile yanlış değeri de söyleyebilir.

Bilmeyenler için, enforce(), bir if ve throw deyimini sarmalayan bir kolaylık işlevidir. Temelde şu etkiye sahiptir ama std.exception modülündeki tanımı daha yetenekli:

void enforce(bool mantıksalİfade, lazy string mesaj)
{
    if (!mantıksalİfade) {
        throw new Exception(mesaj);
    }
}

2) Geçersiz Hamuru değerlerine karşı da final switch daha uygun olabilir:

final switch (pizaHamuru)

Ali
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ı
Yanıtlanan mesaj #11
Salih Dinçer:
Ama spawn() yoluyla, yani dolaylı bir şekilde Thread'ların oluşturulması beni rahatsız ediyor.

spawn()'ın başka işlevlerden farkı yok: bizim her iş parçacığı başlatırken tekrar tekrar yapmamız gereken işlemleri o hallediyor.

Bence D'nin kesinlikle Sean'nın yazdığı şu sınıftan** daha iyi bir şeye ihtiyacı var ya da bu sınıfı kullanmadan Thread'ları birbirleriyle haberleştirmeyi öğrenmeye benim ihtiyacım var. Ama ben bunların nasıl yapılacağını henüz bilmiyorum...:(

** (aslında şu mailbox mantığı gayet güzel

Tabii mailbox mantığı yalnızca "güzel" değil. Mailbox kullanan (yani mesajlaşmaya dayanan) eş zamanlı programlama ile doğrudan veri paylaşımına dayanan eş zamanlı programlama arasında ölçülemeyecek kadar büyük bir fark var:

  • Mesajlaşmaya dayanan eş zamanlı programlama çok kolaydır. Programın nasıl işlediği konusunda kolayca mantık yürütülebilir.

  • Doğrudan veri paylaşımına dayanan eş zamanlı programlamada ise programın doğru yazılmış olduğu ispatlanamaz bile (çok basit programlar hariç).

O yüzden ölçülemeyecek kadar büyük fark diyorum. Birisinin doğru çalışacağından emin olamıyoruz bile. Ne kadar testten geçerse geçsin yine de program kilitlenebilir veya yanlış sonuçlar üretebilir. Şu yazıda ölümlerle sonuçlanmış hatalar da yer alıyor:

  http://en.wikipedia.org/wiki/Race_condition

D'nin std.concurrency modülü modern programlamanın kabul ettiği mesajlaşmayı gerçekleştirir ve çok yakın bir süre önce yazılmış olduğu için de bu konudaki en iyi olarak kabul edilmiş olan arayüzlerden birisini kullanır.

Bunu destekleyen bir örnek de Microsoft'un CCR'ıdır:

  http://msdn.microsoft.com/en-us/magazine/cc163556.aspx

O da heredeyse aynı D'deki mesajlaşma yöntemi benimsenmiştir.

Ali
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ı
acehreli:
Doğrudan veri paylaşımına dayanan eş zamanlı programlamada ise programın doğru yazılmış olduğu ispatlanamaz bile (çok basit programlar hariç).

Tesadüf, D'nin tasarımında da parmağı bulunan Bartosz Milewski'nin bir yazısı daha bugün çıktı:

  http://fpcomplete.com/the-downfall-of-imperative-programmi…

"Race condition"ları bulmaya çalışan bir araç yazımında çalıştığını ve bu işin hiçbir zaman %100 garantili olmadığını söylüyor.

Ama bir düzeltme yapmam gerektiğini farkediyorum: "Doğrudan veri paylaşımına dayanan" demek yetmiyor. Aslında "paylaşılan verinin değiştirilebilen türden" olduğunu da eklemek gerek. Yoksa D'nin immutable anahtar sözcüğü ile korunmuş olan verilerin paylaşılmalarında bir sakınca yok. (Bu, o yazıda geçiyor.)

Ayrıca yazının başlığı da fonksiyonel programlamanın üstünlüğünü gösteriyor: "Emirli Programlamanın Çöküşü".

D temelde bir emirli programlama dili ama fonksiyonel programlama olanakları da sunuyor. Bunlardan olabildiğince yararlanmak gerek.

Ali
zafer #15
Üye Tem 2009 tarihinden beri · 700 mesaj · Konum: Ankara
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #11
Salih Dinçer:
Hatta fırın da yanmayabilir ki önemli olan başta, ortada ve belki sonda karışık bir şekilde (rasgele değil ranfomize fonksiyonundan şimdilik uzak durmalı) herhangi bir işlevin Thread özelliğiyle çağrılması bize bu yapının inceliklerini daha iyi anlamamızı sağlayabilir. Ama spawn() yoluyla, yani dolaylı bir şekilde Thread'ların oluşturulması beni rahatsız ediyor.

Salih anladığım kadarıyla senin esas denemek istediğin kanal (thread) mekanizması o sebeple fırınla, kilerle yada diğer nesnelerle çok zaman harcamak istemiyorsun haklısın tabi ve diğer tarfatan bence proje küçük gibi görünsede aslında çok büyük, benim gördüğüm bir imalat hattının simülasyonunun yapılması ki bence bu gerçekten büyük bir iş :) Ben kendimce bir şeyler yazıyorum eğer işine yararsa sende projene ekleyebilirsin hayır ben başka bir yöntemle veya başka konuda devam edeceğim dersen yazdıklarım bana tecrübe kalır :)

Ancak, program -release seçeneği ile derlendiğinde bütün assert'ler gibi bütün sözleşmeli programlama olanakları da (in, out, ve invariant blokları) koddan çıkartılırlar. (Bunun istisna: assert(false)'lar hiçbir zaman koddan çıkartılmazlar.)

Mesajın girişini okurken evet şimdi gerçekten yeni bir şey öğrendim dedim ama sonra bu kısma gelince sanki tahtaya birşeyler yazıp sonra silgi ile silmiş gibi oldum. Doğrusu invariant'ın ne işe yaradığı konusunda kafam karıştı, çünkü zaten üretimi tamamlanan programları bizler her zaman -release modunda derlemez miyiz? Bu olanaklar sadece programcı için hazırlarmış anlamına mı geliyor?

O yüzden, eğer Firin modülün arayüzündeyse, ya invariant bloğunun yerine ya da onunla birlikte ayrıca kurucuya enforce() denetimleri de eklenmelidir:

Evet, enforce() ekledim ama işi biraz daha çeşitlendirmek adına property kullanımınıda pekiştirmek ve ileride sıcaklığın tekrar değişebileceği ve bunun için sadece kurucu ile sıcaklık ayarı yapılmasının kısıtlayıcı olduğunuda göz önüne alarak bir property ekledim. Ne dersiniz birde siz bakın bakalım hata veya eksikler neler? enforce yerine bir exception sınıfı tanımlamak daha iyi olabilir mi?

import std.stdio;
import std.concurrency;
import std.exception;
import core.thread;
    
enum Hamuru 
{
    ince = 1,   // En çok sevdiğim :)
    orta,
    kalın 
}
 
void main()
{
    writefln("Firin pisirme icin calistiriliyor ...");
    
    Firin pizaFirini = new Firin(5000, 200);
 
    pizaFirini.FiriniYakSicakligiAyarla();
 
    writefln("Firin pisirmeye hazir. Firin sicakligi : %s derece", pizaFirini.VerFirinSicakligi);
}
 
 
class Firin 
{
    private int _firinSicakligi = 0;     // 0..300 birim sıcaklık arasında çalışır.
    private int _mevcutGazMiktari = 0;   // 0..10_000 birim gaz depolanabilir.
 
    public this(int mevcutGazMiktari, int firinSicakligi) 
    {
        _mevcutGazMiktari = mevcutGazMiktari;
        this.YapFirinSicakligi(firinSicakligi);
    }
 
    private void FiriniYakSicakligiAyarla()
    {
        // Her 1 birim sıcaklık için 5 birim gaz yakar.
        int harcananGazMiktari = _firinSicakligi * 5;
 
        assert(harcananGazMiktari < _mevcutGazMiktari, "Firindaki gaz miktari yetersiz.");
 
        // Fırının ısınması 10 saniye sürer.
        IsBitisSuresi(10);
    }
 
    public void PizayiFirinaVer(Hamuru pizaHamuru)
    {
        assert(_firinSicakligi == 0, "Firin sicakligi yetersiz.");
 
        if (_firinSicakligi > 0)
        {
            final switch (pizaHamuru)
            {
                case Hamuru.ince :
                    _mevcutGazMiktari -= 2// Pişirme için harcanan gaz
                    break;
 
                case Hamuru.orta :
                    _mevcutGazMiktari -= 3;
                    break;
 
                case Hamuru.kalın :
                    _mevcutGazMiktari -= 4;
                    break;
            }
 
            //Piza pişiyor..
            IsBitisSuresi(10);   // 10 saniye bekle
        }
    }
 
    private void IsBitisSuresi(int saniye)
    {
        Thread.sleep(dur!("seconds")(saniye));   // saniye kadar bekle
    }
 
    @property int VerFirinSicakligi() const
    {
        return _firinSicakligi;
    }
 
    @property void YapFirinSicakligi(int yeniSicaklikDegeri)
    {
        enforce((yeniSicaklikDegeri >= 0) && (yeniSicaklikDegeri < 300), "Geçersiz fırın sıcaklığı [0..300]");
 
        _firinSicakligi = yeniSicaklikDegeri;
    }
}

Doğrusunu söylemek gerekirse bir önceki Firin sınıfı biraz doğaçlama oldu. Salih'in yazdığı kodu incelerken, birden aklıma geldi ve neden olmasın diyerek kodun içine eklemeye başladım. Daha test dahi etmemiştim. bugün tekrar bir inceledim ve Ali'nin eklediklerinin yanında bir çok hata ve eksik gözüme çarptı, bunları biraz düzelttim biraz da Ali'nin tavsiyelerine uyarak enforce ekledim. Sonuçta yeni kodlar böyle oldu hale eksik ve tutarsız tarafları var.
https://github.com/zafer06 - depo
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-21, 19:41:26 (UTC -08:00)