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. ]