Önceki bir konuda şu foruma gönderme yapmıştım:
http://dfeed.kimsufi.thecybershadow.net/discussion/thread/jgcfs5$2uss$1@digitalmars.com
Orada görüldüğü gibi, sabit uzunluklu dizgilere değer atamak için çeşitli yollar da denedim: copy, fill, insertInPlace, vs.
Onların hepsi aralık işlevleri olduklarından, ve dizgiler de std.array modülü eklendiği sürece aralık olduklarından, örneğin şu programın işleyeceğini umdum:
import std.algorithm;
import std.array;
void main()
{
char[10] d;
copy("merhaba", d); // <-- derleme HATASI
}
Ne yazık ki derlenemiyor:
'Error: template std.algorithm.copy(Range1,Range2) if (isInputRange!(Range1) && isOutputRange!(Range2,ElementType!(Range1))) does not match any function template declaration
Error: template std.algorithm.copy(Range1,Range2) if (isInputRange!(Range1) && isOutputRange!(Range2,ElementType!(Range1))) cannot deduce template function from argument types !()(string,char[10LU])
'
Sorunu çözmek biraz zamanımı aldı. Çözümü ararken izlediğim yolun benzerini aşağıda gösteriyorum.
Hata mesajının içinde std.algorithm.copy şablonunun şablon kısıtlaması da görünüyor:
if (isInputRange!(Range1) && isOutputRange!(Range2,ElementType!(Range1)))
Yani şu iki şartın sağlanması gerekiyormuş:
isInputRange ve isOutputRange (İngilizce olsalar da adları üstünde), aralığın InputRange veya OutputRange olup olmadıklarını denetliyorlar. Şu bölümde tanıtılıyorlar:
http://ddili.org/ders/d/araliklar_baska.html
Şablon parametresi olarak tür verilir ve o tür örneğin InputRange ise true döndürür. Deneyelim bakalım tanımında Range1'e karşılık gelen "merhaba" bir InputRange miymiş... InputRange tür istediği için "merhaba"yı değil, onun typeof ile elde ettiğimiz türünü vermeliyiz:
import std.stdio;
import std.range;
void main()
{
writeln(isInputRange!(typeof("merhaba")));
}
O program 'true' yazdırıyor:
'true'
Güzel. Yani copy() şablonunun Range1 üzerindeki kısıtlamasını aşmış durumdayız. Şimdi diğer kısıtlamaya bakalım: Range2'nin, Range1'in elemanlarının türünü kabul eden bir OutputRange olması gerekiyormuş. Eğer o şart da sağlanıyorsa copy()'nin çağrılamaması için bir neden olmamalı.
Programımızda Range2 olarak 'd' isimli sabit uzunluklu dizgiyi kullanmıştık. Range1'in eleman türü olarak da herhalde immutable(char) doğru olur. Deneyelim:
char[10] d;
writeln(isOutputRange!(typeof(d), immutable(char)));
Ne yazık ki çıktısı 'false' oluyor! Değişebilen char ile deneyelim:
char[10] d;
writeln(isOutputRange!(typeof(d), char));
Yine 'false' oluyor!
Nasıl olur? Bu noktada şunu hatırlıyorum: Diziler OutputRange olarak kullanıldıklarında baş taraflarından kısalırlar. Çok saçma bir işlem gibi gelse de bunun mantıklı bir açıklaması var. Şu bölümde "Dizilerin OutputRange olarak kullanılmaları" başlığında anlatılıyor:
http://ddili.org/ders/d/araliklar.html
Şimdi daha iyi anlıyorum: OutputRange olarak kullanılan dizi başından kısaldığına göre sabit uzunluklu diziler OutputRange aralığı olamazlar! Çünkü sabit uzunlukludurlar, kısalamazlar!
Bu noktada uyanıyorum ve bir dilim kullanmayı akıl ediyorum: Asıl diziyi değil, bir dilimini OutputRange olarak kullanmak gerekir. Dilimler başlarından kısalabilirler ve o yüzden OutputRange olabilirler. Deneyelim:
char[10] d;
char[] dilim = d;
writeln(isOutputRange!(typeof(dilim), char));
Yine olmuyor! Çıktısı yine 'false'. Hmmm? O zaman isOutputRange'i bir kenara bırakıp dilim'i kendim bir OutputRange gibi kullanmaya çalışayım. put() işlemi ile kullanılabilmesi gerekir. Bakalım:
char[10] d;
char[] dilim = d;
put(dilim, 'X');
Yine hata!
'/usr/include/d/dmd/phobos/std/range.d(315): Error: static assert "Cannot put a char into a char[]"'
Nasıl yani? char[] içine char yerleştirilemez mi? range.d'nin o hatayı yazdıran 315 numaralı satırına bakınca sonunda olayı anlıyorum:
static if (is(typeof(r.front = e, r.popFront())))
{
r.front = e;
r.popFront();
}
else static if (isInputRange!E && is(typeof(put(r, e.front))))
{
for (; !e.empty; e.popFront()) put(r, e.front);
}
else
{
static assert(false,
"Cannot put a "~E.stringof~" into a "~R.stringof);
}
Dikkat ederseniz aralığı (yani r'yi) hep .front ile kullanıyorlar. Zaten öyle olmasını da bekleriz: nasıl olsa aralık işlevleri bunlar... Yukarıdaki iki 'static if' deyimi başarısız olmalı ki 'else' bloğu işletilmiş ve hatayı görmüşüz. Şimdi bakalım dilim'in .front'unun türü neymiş:
char[10] d;
char[] dilim = d;
writeln(typeof(dilim.front).stringof);
Çıktısı şaşırtıcı:
'dchar'
Nasıl? char dizisinin eleman türü nasıl dchar olur? Ha ha! :) Bu noktada kendi yazdığım bilgiyi tekrar hatırlıyorum. Şuradaki "std.array modülünün dizgilere özel yararı" başlığına bakın:
http://ddili.org/ders/d/araliklar.html
Çok ilginç ama çok yararlı biçimde, ne tür dizgi olursa olsun, aralık olarak kullanıldıklarında hepsinin eleman türü dchar'dır. Yukarıdaki bağlantıdaki bir sonraki başlık olan "Kendi elemanları bulunmayan aralıklar"a da bakın. Oradaki '"şu".front' örneği de güzel.
Aralıkların char[] ve wchar[] dizgileriyle neden kullanılamadıkları anlaşılıyor: .front'larının döndürdükleri dchar karakterleri kendilerine ait olamazlar! .front'larının döndürdüğü dchar karakterleri, her seferinde ya char'lardan ya da wchar'lardan üretilen geçici değerlerdir. (Aslında bir sağ değerdir (rvalue).) O dchar'a atama yapılamaz. O yüzden char[] gibi bir dilimi OutputRange olarak kullanamıyoruz.
Çok uzun yazdım ama aydınlandım. :)
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]