Alıntı (zafer:1319272041):
> Öncelikle ByLine() yapısı içindeki yapılan işlemler sonucunda dosyadan bir satır okunuyor ve bu satır bilgisini kendi üye değişkeni olan line üzerinde depoluyor.
Doğru.
Alıntı:
> Ardından isteyen kişiye bunu gönderiyor.
Evet. ByLine bir InputRange olduğu için de foreach perde arkasında front()'u çağırıyor ve foreach'in 'satir' değişkeni olarak bize veriyor.
Alıntı:
> Gönderiyor ama gönderdiği bir char[] dizisi olduğu için aslında bilgiyi değil bu bilgiye erişim sağlayacak bir referans gönderiyor.
Doğru. Diziler referanstırlar. Asıl bilgi D çalışma ortamı (runtime) tarafından sahiplenilmiştir; diziler ona erişim sağlarlar.
Not 1: Dizi diyoruz ama aslında dilim demek daha doğru.
Not 2: 'line' dinamik bir dizi olduğu için asıl bilginin D çalışma ortamı tarafından sahiplenilmiş olduğunu söylüyoruz. Sabit uzunluklu dizi olsa bilgiye dizinin kendisinin sahip olduğunu düşünmek doğru olur.
Alıntı:
> Bundan dolayı foreach'in satır değişkeni (gerçekte böyle olmasada) bir referans değişkeni gibi davranmaya başlıyor.
Doğru.
Alıntı:
> Burada satir.ptr değerinden hareketle tüm satırların aynı adresi göstermesi buna bir kanıt olabilir mi?
satir.ptr değerlerinin aynı çıkması hepsinin ilk karakterlerinin aynı yerde bulunduğunu gösterir. (Ama örneğin dizilerin aynı olduklarını göstermez; çünkü uzunlukları farklı olabilir.)
Alıntı:
> Durum böyle olsa bile bu liste dizisi ile ilgili sorunu açıklamıyor. Neden? Çünkü çıktıda da görüldüğü gibi adresler aynı olsada aslında her düngüde dosyadan istediğimiz satır bilgisini alıyoruz ve bu bilgi çıktıda da doğru görünüyor. Buradan hareketle aslında ben liste dizisine her defasında doğru değeri ekliyorum diye düşünüyorum, haklı mıyım?
Dosyadan her okuduğunda 'line' gerçekten de son okunan satırı içerir. Sen listeye eklediğinde de gerçekten son satırı eklersin. Ama dikkat edersen 'line' ve senin liste elemanın aynı bilgiyi paylaşıyorlar. Bu bir paylaşım olduğu için 'line' bilgiyi değiştirdikçe liste elemanlarının eriştirdiği veriler de değişmiş oluyor.
Alıntı:
> Eğer durum böyleyse, yani liste dizisinin her elemanı doğru ise nasıl oluyorda en son eleman dizinin tüm elemanlarına ulaşıp kendisi ile değiştiriyor. Bu kısmı bir türlü anlamadım.
Çünkü tek veri var ve o veri son okunan satırı içeriyor. Ancak, senin verilerinin uzunluklarının birbirlerine bu kadar yakın olmaları da akıl karıştıran önemli bir unsur. Derinlemesine incelemedim ama ByLine readln'ı kullanıyor. readln da sanıyorum bir eniyileştirme olarak eğer satırda zaten yer varsa öncelikle oraya yazıyor.
Olayı daha basit bir kodla görelim. Kısa olsun diye yalnızca iki satır olsun:
'Book:Defter
Table:Masa'
import std.stdio;
void main()
{
string[] liste;
// --- Birinci satır ---
char[] line = "Book:Defter".dup;
string satir_0 = cast(string)line;
liste ~= satir_0; /* Ayrıca not: Önce uzunluğu arttırıp sonra '= satir'
yapmak yerine ~= daha kısa ve bence açık. */
// --- İkinci satır ---
//
// "Table:Masa" için 'line'da yer olduğu için teker teker var olan
// karakterlerin üstüne yazılıyor. Bu iş için bir C sistem kütüphanesi
// kullanıyor olabilirler ama sonuçta tahminim doğruysa 'line'ın var olan
// karakterlerinin yerine yeni satırın karakterleri geliyor
assert(line.length >= "Table:Masa".length);
foreach (i, karakter; "Table:Masa") {
line[i] = karakter;
}
// Ve o işlemin sonucunda 'liste' bozuldu bile:
writeln(liste);
}
Çıktısı:
'["Table:Masar"]'
satir_1'i de listeye ekleyen üç kod satırı daha ekleyerek devam edelim:
foreach (i, karakter; "Table:Masa") {
line[i] = karakter;
}
line.length = "Table:Masa".length;
string satir_1 = cast(string)line;
liste ~= satir_1;
Şimdiki çıktısı:
'["Table:Masar", "Table:Masa"]'
Dikkat edersen dizinin ilk elemanı hâlâ "Book:Defter"in son r'sini de görüyor.
Şu anda elimizde 'line' ve 'liste' açısından ne bulunduğunu çizeceğim:
'
veri: Table:Masar
^
|
line: |
ptr ---------|
length==10 |
|
liste |
======== |
eleman0: |
ptr --------|
length==11 |
======== |
eleman1: |
ptr --------
length==10
'
Yani sanıyorum bu iş aslında readln'ın 'line'ın karakterlerinin başkaları tarafından paylaşıldığından haberinin olmamasından kaynaklanıyor. Bunun bir eniyileştirme olduğunu düşünebiliriz; kopyalanması gerekmiyorsa bellekten boş yere yeni yer ayrılması gerekmiyor.
Bu arada, şu tür dönüşümü bana sağlam gelmedi:
satir1 = cast(string)satir;
Değişebilen bir diziyi "bunun karakterlerinin artık değişmeyeceğinden eminim" diyerek string'e dönüştürmek doğru değil. Burada yaşadığımız sorunda da olduğu gibi, 'satir'ın sahibi onun karakterlerini değiştirdiğinde satir1 de değişmiş olur.
import std.stdio;
void main()
{
char[] değişebilen;
değişebilen ~= "abc";
// Güvensiz tür dönüşümü!
string değişmez = cast(string)değişebilen;
değişebilen[0] = 'A'; // KÖTÜ: değişmez de değişir!
}
Bence burada en iyisi .idup kullanmak. Zaten o zaman bütün sorun da ortadan kalkar:
satir1 = satir.idup;
Not: Benim önerdiğim cast güvensiz değil, çünkü o cast split'in üreterek döndürdüğü sonuca uygulanıyordu:
string[] soru = cast(string[])split(satir.dup, ":");
Üstelik satir.dup ile kopyasını da aldığımız için split'e verilen o kopyanın da değiştirilmediğini kendimiz biliyoruz. Böyle kesinlikle emin olduğumuz durumlarda değişebilen türlerden immutable'a dönüşüm yapabiliriz.
Tabii ByLine da döndürdüğü değeri hiç değiştirmeyeceğini belgelemiş olsaydı o zaman tür dönüşümü kullanabilirdik.
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]