Yapıların ve sınıfların toString() işlevlerinin tasarımı hız açısından iyi değildir:
string toString() const
{
// ... bir string oluştur ve döndür ...
}
Aslında mantık olarak doğrudur: Nesnenin string olarak ifadesi döndürülmektedir. Ancak, iç içe nesneler kullanıldığında bütün üyelerin toString()'lerinin çağrılmaları ve onların döndürdükleri string'lerden oluşan yeni bir string döndürülmesi yavaş kalabilir.
Aşağıdaki programdaki Poligon nesnesi RenkliNokta'lardan oluşuyor. RenkliNokta da Renk ve Nokta'yı bireştiriyor:
import std.stdio;
import std.string;
import std.format;
import std.array;
struct Nokta
{
int x;
int y;
string toString() const
{
return format("(%s,%s)", x, y);
}
}
struct Renk
{
ubyte r;
ubyte g;
ubyte b;
string toString() const
{
return format("RGB:%s,%s,%s", r, g, b);
}
}
struct RenkliNokta
{
Renk renk;
Nokta nokta;
string toString() const
{
return format("{%s;%s}", renk, nokta);
}
}
struct Poligon
{
RenkliNokta[] noktalar;
this(RenkliNokta[] noktalar...)
{
// NOT: BU KOD HATALI! Aşağıda 6 numaralı yorumda açıklanıyor.
this.noktalar = noktalar;
}
string toString() const
{
return format("%s", noktalar);
}
}
void main()
{
auto p = Poligon([ RenkliNokta(Renk(10, 10, 10), Nokta(1, 1)),
RenkliNokta(Renk(20, 20, 20), Nokta(2, 2)),
RenkliNokta(Renk(30, 30, 30), Nokta(3, 3)) ]);
writeln(p);
}
main() içindeki writeln() çağrısı, her RenkliNokta için üç string'den toplam dokuz string oluşmasına neden olur. O da, akıllıca davrandığımız ve format'tan yararlandığımız için öyledir... Yoksa, aşağıdaki gibi eklemeler daha da fazla string oluşmasına neden olurdu:
import std.conv;
// ...
struct RenkliNokta
{
Renk renk;
Nokta nokta;
string toString() const
{
// Beş string'in eklenmesi ve altıncı bir string'in döndürülmesi!
return "{" ~ renk.to!string ~ ";" ~ nokta.to!string ~ "}";
}
}
(Not: Tabii dizi eklemelerinde D olabildiğince hızlıdır. Sonunda boş yer olan diziye eklemek hızlı bir işlemdir.)
Phobos'a bir kaç sürüm önce eklenen ve şimdilik yalnızca BigInt tarafından kullanılan bir toString() yüklemesi daha var(mış). Aslında ben duyuyordum ama daha önce hiç denememiştim.
Bu olanak aslında formattedWrite'a ait: C++'ın akımlarındaki mantığa sahip. Tam olarak da std::ostringstream'in eşdeğeri olarak görülebilir. ostringstream, kendisine yazılan değerleri içindeki tek std::string'e ekler:
// C++ kodu
ostringstream cikis;
cikis << 42 << "merhaba"; // icindeki string bUyUr
cout << cikis.str(); // olusturdugu string'e sonunda .str() ile erisilir
D'de bunu sağlayan toString() yüklemesi şöyle:
void toString(scope void delegate(const(char)[]) hedef) const
{
// ... formattedWrite ile hedef'e yaz ...
}
(İngilizce belgelerde 'hedef' yerine 'sink' deniyor.)
Çok korkutucu görünüyor :) ama kullanımı bildiğimiz toString() kadar basit. Tek yapılması gereken, format()'ın ürettiğini return ile döndürmek yerine, formattedWrite ile hedef'e yazdırmak:
struct Nokta
{
// ...
void toString(scope void delegate(const(char)[]) hedef) const
{
formattedWrite(hedef, "(%s,%s)", x, y);
}
}
struct Renk
{
// ...
void toString(scope void delegate(const(char)[]) hedef) const
{
formattedWrite(hedef, "RGB:%s,%s,%s", r, g, b);
}
}
struct RenkliNokta
{
// ...
void toString(scope void delegate(const(char)[]) hedef) const
{
formattedWrite(hedef, "{%s;%s}", renk, nokta);
}
}
struct Poligon
{
// ...
void toString(scope void delegate(const(char)[]) hedef) const
{
formattedWrite(hedef, "%s", noktalar);
}
}
Sonuçta, main()'deki writeln(p) çağrısı toString'in bu yüklemesini algılıyor, sonuna eklemekte olduğu kendi içindeki tek string'i hedef olarak kullanan bir temsilci oluşturuyor, ve o temsilciyi 'p' nesnesinin toString()'ine gönderiyor. formattedWrite da öyle bir temsilciye eklemeyi bildiğinden bize yalnızca düzeni belirtmek kalıyor.
'hedef' elden ele bütün türlere geçtikçe eklenen karakterler hep aynı dizginin sonuna ekleniyor. Sonuçta da nesnelerin string karşılıkları çok daha hızlı bir biçimde oluşturulabiliyor.
(Bunu da kitabın uygun bir yerine eklemem gerekiyor.)
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]