Yukarıda şöyle demiştim:
// Bunun için daha etkin yöntemler var. Burada çok basitçe:
beklenenler = beklenenler.filter!(b => b.id != int.min).array;
Diziden eleman çıkartmanın etkin bir yöntemi, std.algorithm.remove'u (program için kabul edilebiliyorsa SwapStrategy.unstable ile) kullanmaktır:
import std.random;
import std.range;
import std.algorithm;
import std.stdio;
auto rasgeleSayılar(size_t adet) {
return iota(adet).map!(_ => uniform(0, 100));
}
void main() {
auto a = rasgeleSayılar(20).array;
writefln("Önce : %s", a);
auto b = a.remove!(e => e % 2);
// a'nın yerinde değiştiğini kanıtlayalım:
assert(a.ptr == b.ptr);
writefln("Sonra: %s", b);
}
assert'ün gösterdiği gibi, a yerinde değişmiştir. Yani, b yeni bir dizi değildir.
'
Önce : [91, 98, 89, 26, 85, 93, 9, 19, 99, 13, 82, 5, 36, 22, 51, 71, 48, 71, 37, 76]
Sonra: [98, 26, 82, 36, 22, 48, 76]
'
Eğer elemanların sıraları önemli değilse şu genelde daha hızlıdır:
auto b = a.remove!(e => e % 2, SwapStrategy.unstable);
Dikkat ederseniz, çıkartılan 89'un yerine sondaki 46 gelmiş (böylece elemanların kopyalanmaları aza indirgenmiştir), vs.:
'
Önce : [89, 95, 83, 15, 70, 99, 23, 37, 57, 70, 39, 21, 29, 80, 98, 63, 79, 59, 48, 46]
Sonra: [46, 48, 98, 80, 70, 70]
'
Her iki yöntemde de a degişmiştir ve herhalde artık kullanışlı değildir. (a'nın son tarafındaki elemanların bazıları a'nın baş tarafındakilerle aynıdır.) Dolayısıyla, normalde sonuç doğrudan a'ya atanın (ben farkını gösterebilmek için b tanımladım):
a = a.remove!(e => e % 2, SwapStrategy.unstable);
Şimdi bir sorun, a'ya yeni eleman eklemeye kalktığımızda ortaya çıkabilir çünkü druntime a'nın sondaki elemanlarının kimse tarafından kullanılmamakta olduğunu bilemez ve yeni eleman eklediğimizde bütün diziyi yeni bir yere taşır:
writefln("Eleman eklemeden önce : %s", a.ptr);
a ~= 222222;
writefln("Eleman ekledikten sonra: %s", a.ptr);
Elemanlar artık yeni bir yerdedir :(:
'
Eleman eklemeden önce : 7FCC6E782080
Eleman ekledikten sonra: 7FCC6E784000
'
a'nın başka kimse tarafından kullanılmadığından eminsek assumeSafeAppend'i çağırabiliriz. assumeSafeAppend, dilimin kapasitesini yerinde arttırır. (Örneğin, kapasite en azından a'nın en baştaki uzunluğu kadar olacaktır.) (Ancak, bunu gösterebilmek için çok debelendim çünkü henüz giderilmemiş olan bir hata var: array() ile oluşturulan dilimlerin kapasiteleri hep 0 kalıyor. O yüzden örneği varolan bir dilimin sonuna eleman ekleyerek başlatacak biçimde değiştiriyorum.)
import std.random;
import std.range;
import std.algorithm;
import std.stdio;
auto rasgeleSayılar(size_t adet) {
return iota(adet).map!(_ => uniform(0, 100));
}
void main() {
// Değişiklik: .array çağırmak yerine bir diziye ekleyerek başlıyorum
int[] a;
rasgeleSayılar(20).each!(e => a ~= e);
writefln("Önce : %s", a);
a = a.remove!(e => e % 2, SwapStrategy.unstable);
writefln("Sonra: %s", a);
writefln("Eleman eklemeden önce : %s (kapasite: %s)", a.ptr, a.capacity);
assumeSafeAppend(a);
a ~= 222222;
writefln("Eleman ekledikten sonra: %s (kapasite: %s)", a.ptr, a.capacity);
}
Artık a'ya yeni eklenen eleman yerinde eklenmiştir çünkü assumeSafeAppend a'nın elde edebileceği bütün kapasiteyi ona vermiştir. (Dikkat: Artık işimize yaramayan a'nın sonundaki eski elemanlara eriştiren başka dilim bulunmadığını kendimiz sağlamalıyız.)
'
Eleman eklemeden önce : 7F3B51E15080 (kapasite: 0)
Eleman ekledikten sonra: 7F3B51E15080 (kapasite: 31)
'
Tabii bütün bu işlemleri otomatik olarak halleden bir tür kullanmak isteyebiliriz:
import std.random;
import std.range;
import std.algorithm;
import std.stdio;
struct YerindeDizi(T) {
T[] elemanlar;
void ekle(T e) {
elemanlar ~= e;
}
void sil(alias kıstas, SwapStrategy swapStrategy = SwapStrategy.unstable)() {
elemanlar = elemanlar.remove!(kıstas, swapStrategy);
elemanlar.assumeSafeAppend();
}
// Dikkat: 'elemanlar' üyesine doğrudan erişime izin verilmemelidir çünkü
// bu türü kullananların ellerindeki dilimler assumeSafeAppend ile geçersiz
// (veya kullanışsız) hale gelebilir.
//
// Ben burada daha fazlasını gerçekleştirmeyeceğim.
}
auto rasgeleSayılar(size_t adet) {
return iota(adet).map!(_ => uniform(0, 100));
}
void main() {
auto a = YerindeDizi!size_t();
foreach (i; 0 .. 100) {
const önceki_ptr = a.elemanlar.ptr;
rasgeleSayılar(20).each!(e => a.elemanlar ~= e);
// (Bunu bir isimsiz işlev (lambda) yerine static bir yerel işlev olarak
// tanımlamak zorunda kaldım. dmd'nin bilinen bir yetersizliği...)
static bool kıstas(size_t eleman) {
return eleman % 2;
}
a.sil!(kıstas, SwapStrategy.unstable);
if (a.elemanlar.ptr != önceki_ptr) {
writefln("Yer değişti - ptr: %s, length: %s, capacity: %s",
a.elemanlar.ptr, a.elemanlar.length, a.elemanlar.capacity);
}
}
writefln("Son - ptr: %s, length: %s, capacity: %s",
a.elemanlar.ptr, a.elemanlar.length, a.elemanlar.capacity);
}
Sonuçta, başta gösterdiğim 'beklenenler' gibi yalnızca bir kaç eleman taşıyacak olan diziler yerine buradaki gibi YerindeDizi gibi bir tür kullanılırsa elemanlar hiç kopyalanmazlar. Yukarıdaki programda 100 kere 20 eleman ekleyip tek sayı olanlarını çıkartıyorum. Dolayısıyla bu dizi gerçekçi olmayan biçimde hep büyüse de (ortalamada 1000 eleman içerir) elemanlar yalnızca 5 kere kopyalanmak zorunda kalıyorlar:
'
Yer değişti - ptr: 7F01B8778100, length: 7, capacity: 31
Yer değişti - ptr: 7F01B877E000, length: 26, capacity: 63
Yer değişti - ptr: 7F01B877F000, length: 59, capacity: 127
Yer değişti - ptr: 7F01B877C800, length: 122, capacity: 255
Yer değişti - ptr: 7F01B7879010, length: 255, capacity: 509
Son - ptr: 7F01B7879010, length: 1007, capacity: 1021
'
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]