Thread overview
Tür Nitelikleri ve Çöp Değer
Sep 08, 2020
rkaratas
Sep 08, 2020
kerdemdemir
Sep 09, 2020
rkaratas
Sep 09, 2020
rkaratas
Sep 09, 2020
rkaratas
Sep 10, 2020
rkaratas
September 08, 2020

Merhabalar forumda ki ilk sorumu sorayım :)
Ali abinin D kitabını okuyorum ve her şey kafamda gayet net bir şekilde oturmuş durumda şuana kadar. Sadece küçük bir kaç tane soru sormak istiyorum. Birinci sorum tür bilgisi için kullandığımız int, double, short gibi anahtar sözcüklerin o türlere ilişkin başka bilgileri de kullanabilmemize imkan verebilmeleriyle alakalı. Örneğin ben int.max yazdığım zaman bana, bu türün tutabileceği maksimum değeri veriyor.(Çok güzel kullanışlı bir özellik olmuş). Sanırım c++ da derleyiciye bağlı değiştiği için limits isimli bir başlık dosyasında tutuluyordu. Burada ki çözüm çok hoş olmuş. Sorum şu arkada bu değerlere nokta operatörüyle erişiyoruz da, bu anahtar sözcükler birer obje gibi mi ele alınıyor? Bunlar aynı zamanda birer objeler mi? Diğer bir sorumda burada temel türler hayata genel olarak 0 değeri ile başlıyorlar ki bu da bence çok güzel olmuş. C++ da yerel değişkenler çöp değer ile hayata başlıyorlardı ki ben bunu hep performans odaklı bir dil olmasından dolayı yaptıklarını düşünüyordum. D dilinde bu seçimin bir anlamı var mıdır onu merak ettim. Çöp değerle başlatmamak elbette ki daha iyi bir seçimdir ancak performans kaybı olarak düşünmem yanlış mı? İkinci soru biraz saçma olabilir kusura bakmayın, herkese iyi günler dilerim :)

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

September 08, 2020

Alıntı:

>

Sanırım c++ da derleyiciye bağlı değiştiği için limits isimli bir başlık dosyasında tutuluyordu

Bencede "primitive" türlere .max özelliğini vermeleri güzel bence.

Bunun C++ karşılığı baya uzun:

std::numeric_limits::max()

Alıntı:

>

Sorum şu arkada bu değerlere nokta operatörüyle erişiyoruz da, bu anahtar sözcükler birer obje gibi mi ele alınıyor?

Yok C++ STL 'de yaptığı bir şeyi D bence daha iyi tercih yapıp dilin çekirdeğinde ele almış. Ortada bir obje yok. int bildiğimiz düz int.

Konu açılmışken D'de .init diye birşey daha var. Türlere "default" değerleri atıyor yani resetleyebiliyorsun. Şurda bu .init ile ilgili güzel birşeyler tartışmıştık eskiden:
http://ddili.org/forum/thread/1712

Alıntı:

>

Çöp değerle başlatmamak elbette ki daha iyi bir seçimdir ancak performans kaybı olarak düşünmem yanlış mı?

Bu performans kaybı artık günümüz koşullarında önemli değil artık . C++ bu davranışının( bir çok konuda olduğunu gibi) en güzel açıklaması "historical reasons" yani C dilinin üstüne kurulmuş olması. Ya Ali abi bilirde D'de de bir yolu vardı galiba buna değer atama vakit kaybetme demenin ama ben hatırlayamadım şimdi. Ama dediğin D "default" olarak değer atıyor ve kendisinden allah razı olsun bence iyi yapıyor.

Performans konusunda bellek veya daha kötüsü disk okuma/yazma işlemleri , cache miss, verimsiz algoritmalar , yanlış veri yapısı kullanımı, gereksiz kopyalama işlemleri, yanlış donanım (GPU/CPU) performans sorunlarının %95'i kapsar desem çok yalan olmaz sanırım. Rakamda söyleyince çok atmaca bir bilgi oldu ama genel bilgi vermek için belirtmek istedim.

Alıntı:

>

İkinci soru biraz saçma olabilir kusura bakmayın

İki sorunda çok iyi lütfen daha çok sor. Hatta benim aklıma da bir soru geldi bunun üstüne şimdi.
Diyelim çok özel bir ortamdayız bu ortamda integerlar 1 byte. Bu ortamda C++ derleyicisi için #include değiştiririz olur biter. Fakat D böyle bir ortamda ne yapıcak. Bu özel ortamda D derleyicisi geliştirmek isteyen bir firma int.max 'ı 1 yapabilmek için nasıl D çekirdeğini değiştirecek?

Ekmek teknesi vardı eskiden böyle Heridot Cevdet diye bir tip hikayeden bir şey anlatır sonra gelen soruyu cevaplayamaz sonunda toplanır Ekmek teknesine giderlerdi Nusret baba'ya sormak için. Evet şimdi toplanıp Ali Abi'ye soralım bizde :) .

Erdemdem.

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

September 08, 2020

Alıntı (rkaratas):

>

Merhabalar forumda ki ilk sorumu sorayım :)

Teşekkürler rkaratas. Erdem'in de dediği gibi, böyle soruları seviyoruz. :)

Alıntı:

>

bu anahtar sözcükler birer obje gibi mi ele alınıyor?

Çalışma zamanında hiç masraf yok; derleme zamanında hazır değer olarak kullanılıyorlar.

Alıntı:

>

temel türler hayata genel olarak 0 değeri ile başlıyorlar

Ufak bir düzeltme: karakter ve kesirli sayı türleri bu konuda şanslılar çünkü tamsayı türlerinin aksine, o türlerin "geçerli değil" veya "ilklenmemiş" anlamına gelen ilk değerleri var. Örneğin, double'ın ilk değeri NAN (not a number).

Tamsayılarda böyle özel bir ilk değer bulunmadığından gerektiğinde Nullable'ı kullanıyoruz: https://dlang.org/phobos/std_typecons.html#Nullable

Alıntı:

>

C++ da yerel değişkenler çöp değer ile hayata başlıyorlardı ki ben bunu hep performans odaklı bir dil olmasından dolayı yaptıklarını düşünüyordum.

Dizi dışına erişme hataları gibi ilklenmemiş değişkenler de program hatası nedenlerinin başında geliyor. Bu açıdan bakınca ufak bir atama işleminden kaçınmak bence sorumsuzluk olarak kabul edilmeli. Zaten C++ ile ilgili çoğu (bütün?) programlama öğütleri ve ilkeleri de "her değişkeni ilkleyin" derler.

Alıntı:

>

D dilinde bu seçimin bir anlamı var mıdır onu merak ettim.

Program doğruluğu. :) Ayrıca, durum D'de C++'tan biraz daha iyi çünkü bütün ilk değerler derleme zamanında bilinmek zorunda olduğundan örneğin yapılar için (kullanıcı özellikle kurucu işlev belirtmemişse) hiç ilkleme atamaları yapılmıyor. Onun yerine yapının ilk değerini taşıyan bir bellek parçası olduğu gibi kopyalanıyor.

Aşağıdaki kod D'nin ilk değerleri bile nasıl derleme zamanında işlettiğini gösteriyor:

struct A {
 double d = 7.5;
 B b = bİlkDeğeri();    // <-- Derleme zamanında işletilen ifade
}

struct B {
 version (falanca) {
   // Bu değeri görmek için programı -version=falanca ile derleyin
   int i = 0x1111_1111;

 } else {
   int i = 0x2222_2222;
 }
}

B bİlkDeğeri() {
 // Böyle bir işleve normalde hiç gerek yok; bu D olanağını göstermek için kullanıyorum.
 return B(0x3333_3333);
}

int main() {
 // Derleme zamanında yazalım
 pragma(msg, A.init);

 // Bir de çalışma zamanında kullanalım:
 auto a = A();

 // (Sıfırdan farklı bir değer döndürdüğümüz için program hatayla sonlanmış gibi olacak ama olsun.)
 return a.b.i;
}

Bu kodu Godbolt.org gibi bir sitede de derleyebilirsiniz ama ben D ile gelen obj2asm programını kullanacağım:
'
$ dmd -c deneme.d
A(7.5, B(858993459)) <-- Derleme zamanında yazdırılan değer (858993459 == 0x3333_3333)

$ obj2asm deneme.o > deneme.asm
'

deneme.asm dosyasını açtığımızda içine gömülmüş olan şu bölümleri görüyoruz:
'
rodata segment
_D6deneme1A6__initZ: <-- A'nın ilk değeri
db 000h,000h,000h,000h,000h,000h,01eh,040h ;.......@
db 033h,033h,033h,033h,000h,000h,000h,000h ;3333.... <-- A'nın 'b' üyesi (i: 0x3333_3333)

_D6deneme1B6__initZ: <-- B'nin ilk değeri
db 022h,022h,022h,022h,000h,000h,000h,000h ;"""".... <-- B'nin 'i' üyesi (0x2222_2222)
db 000h,000h,000h,000h,000h,000h,01eh,040h ;.......@
'
Aynı dosyada daha sonra main() içinde o gömülü değerin doğrudan kullanıldığını görüyoruz. Yani, çalışma zamanında tek kopyalama söz konusu:
'
_Dmain:
push RBP
mov RBP,RSP
sub RSP,020h
mov dword ptr -014h[RBP],0
movsd XMM0,_D6deneme1A6__initZ@PC32[RIP] <-- Burada
movsd -020h[RBP],XMM0
mov EAX,033333333h <-- Programın döndürdüğü hazır değer
mov -010h[RBP],EAX
mov -018h[RBP],EAX
leave
ret
add [RAX],AL
text._Dmain ends
'
Ek olarak, çalışma zamanı performansına önem veriyorsak derleyici olarak dmd değil, ldc veya gdc kullanıyoruz. ;)

Erdem'in hatırlattığı "ilklememe" konusu için '=void' kullanıyoruz:

struct S {
 double d = void;
}

Ancak, yakın zaman önce öğrendiğime göre, d'nin değeri double.nan değil, yine de 0.0 oluyormuş. Bu bizim için sabit uzunluklu dizi konusunda önemli olmuştu. Elimizde double[10_000] türünde bir üye olsa programa gömülü 10 bin tane double değer oluyor. O üyeyi =void ile ilkleyince bu sefer de 10 bin adet atama işlemi oluyor! :) Üstelik, biz ilk değerleri 0.0 değil, yine de double.nan istiyoruz.

Bunun için aklıma gelen çözüm şu:

import std;

struct Init {}

struct A {
 // Programa 10_000 değer gömülmesin diye '= void'
 double[10_000] a = void;

 // Derleyicinin yazdığı varsayılan kurucuyu etkisiz hale
 // getiriyoruz.
 @disable this();

 // D'de kullanıcı varsayılan kurucu yazamadığından Init
 // diye özel bir tür alan kurucu yazıyoruz. Biz bunu
 // "varsayılan" kurucu olarak kullanacağız.
 this(Init) {
   // Bütün elemanların double.nan olmalarını istiyoruz
   a[] = double.nan;
 }
}

void main() {
 auto a = A(Init());
 assert(a.a[42].isNaN);
 // Güzel: hepsi nan olmuş. :)
}

Ama büyük bir sorun olmasa da her seferince A(Init()) yazmak garip. Bunun için de şöyle bir 'template'ten yararlanmayı düşündüm:

template varsayılan(T) {
 enum varsayılan = T(Init());
}

void main() {
 auto a = varsayılan!A;
 assert(a.a[42].isNaN);
 // Güzel: hepsi nan olmuş. :)
}

Her nedense o çözüm her istediğimi karşılıyor:

  • Program küçük (içine gömülü 10_000 double) yok
  • Program kodunda 10_000 adet işlemi yok
  • double elemanların değeri double.nan
  • Başka türler için de kullanabiliriz

D'yi övüp duruyoruz ama 'varsayılan' gibi bir çözüm bulup yazabilmek bunun bir kanıtı. :)

Ali

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

September 09, 2020

Teşekkür ederim cevaplarınız için.

D dilinde işler biraz daha farklı yapılıyormuş ama cidden akıllıca olmuş. Evet C++'da öğüt her zaman, ilk değer ile bir nesneyi hayata başlatmak üzerinedir. Gayet güzelmiş performans açısından da.

Alıntı:

>

struct A {
// Programa 10_000 değer gömülmesin diye '= void'
double[10_000] a = void;

Burası ile ilgili küçük bir sorum olacaktı. "=void" yaptığımızda 10 000 tane nesne programa yerleştirilmedi değil mi? Yani programı şişirmedik.

Alıntı:

>

this(Init) {
// Bütün elemanların double.nan olmalarını istiyoruz
a[] = double.nan;
}

Bu varsayılan kurucu işlev sayesinde de A Struct'ı türünden bir obje yaratılırken, içinde ki a dizisi değerleri, double.nan olan elemanlar olacaklar sanırım. Eğer biz bu varsayılan kurucu işlevi yazmasaydık, a dizisinin öğeleri 0.0 ile mi hayata başlayacaklardı? Bunu sormak istedim, teşekkür ederim.

Rohat.

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

September 09, 2020

Heh şimdi tam olarak anladım abi :-D teşekkür ettim. C++ da genelde dinamik dizi(vector) kullanıyoruz dediğin gibi, sabit uzunluklu dizi pek kullanmamıştım. Benim için baya bilgilendirici oldu teşekkür ederim.

Rohat

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

September 09, 2020

Konuya devam etmemiz güzel olmuş.

Şimdi abi dilim arayüzü, değer türü ve referans türleri ne demek tam kestiremedim :) Dilim arayüzüyle kastettiğimiz sanırım, bir indeks ile erişilen bir arayüz sağlayan, veri yapıları olması.

Şimdi C++ kafasından bakarsam değer türü kopyalama ile değer alırken, referanslar değerin kendisini ediniyorlardı. Yani sabit diziler doğası gereği bir değer türü ve dinamik diziler ise referans türü mantığında mı çalışıyor? Bir de C++ da dinamik dizi, değerleri Heap'te tutuyordu sabit diziden farklı olarak.

Rohat

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

September 09, 2020

Evet, '=void' yazınca 10_000 double'dan oluşan bir ilk değer oluşmuyor. Ama öyle yapınca derleyicinin yazdığı kurucu işlev 10_000 adet atama işlemi içeriyor! Ne anladım ben bu işten. :p (Başka derleyiciler öyle yapmıyor olabilirler.)

Programda 10_000 adet atama işlemi de olmaması için bir yol aradım ve otomatik kurucu işlevi ortadan kaldırmak için onu "@disable ettim".

Bundan sonra da '=void'in başka bir azizliğiyle uğraştım: double'ın ilk değeri normalde double.nan olduğu halde '=void' yapınca 0.0 oluyor. Bunu isteyenler de olabilir ama ben normal davranması için ne yapılması gerektiğini bulmaya çalıştım ve kendim bir kurucu işlev yazmak zorunda kaldım.

Alıntı:

>

Bu varsayılan kurucu işlev sayesinde

Programcı D'de struct'lara varsayılan kurucu işlev yazamaz. O yüzden Init diye bir yapı uydurup onu varsayılan kurucu gib kullandım. Her noktada A(Init()) dememek için de varsayılan!A diye bir söz diziminin nasıl göründüğüne baktım.

Bunların hiçbirisi normalde önemli değil. Kimse böyle şeylerle uğraşmak zoranda kalmıyor çünkü normalde sabit uzunluklu dizi kullanılmıyor ve kullanıldığında da uzunluğu bir kaç adetle sınırlı kalıyor. Ama benim iş yerinde tam da böyle bir sorunun var çünkü kullandığımız ROS türlerinde çok sayıda ve çok uzun sabit uzunluklu dizi bulunuyor. Bu deneyleri iş yerindeki programların boyutlarını küçültmek için yaptım. Sizi korkutmasın. :)

Ali

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

September 09, 2020

Söz açılmışken devam edelim. :)

Her ikisine de dilim (slice) arayüzüyle erişiliyor olsa da D'de sabit uzunluklu dizilerle dinamik diziler (std::vector benzeri) bambaşka türler. Hatta, dinamik dizilerin programda dilim arayüzünden başka varlıkları yok.

Örneğin, sabit diziler değer türü oldukları halde dinamik diziler referans türü: Sabit dizi işlev parametreleri kopyalanırlar (binlerce elemanlarıyla birlikte).

Ali

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

September 09, 2020

Doğru.

Emin olmak için şu sayfada "değer türü"ne (veya "referans türü"ne) tıklıyorum:

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

Ve şurayı buluyorum:

http://ddili.org/ders/d/deger_referans.html#ix_deger_referans.de%C4%9Fer%20t%C3%BCr%C3%BC

;)

Ali

Not: Kitabın İngilizce dizini de şu: http://ddili.org/ders/d.en/ix.html

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

September 10, 2020

Mesaj alındı abi :-)

Böyle bir dizin yapısı olduğunu bilmiyordum. Burayı kullanırım bol bol. Herkese çok teşekkür ederim verdikleri cevaplar için.

İyi günler dilerim.

Rohat

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