Şu konuda erişicilerle aralıkları karşılaştırmıştık:
http://ddili.org/forum/thread/330
TDPL'de tam bu konuya değinen bir bölüme rastladım. ("The D Programming Language" kitabına biz de kısaca TDPL diyebiliriz herhalde. :))
Yazar, işlevleri anlatmaya 'find' işlevi ile başlıyor. Kitabın o bölümünü burada özetleyeceğim. (Tabii inatla Türkçe kodla. :D)
Bu kitabın zaten programcılık bilenlere yönelik olduğunu konuşmuştuk. Zaten bilinen kavramların D'de nasıl oldukları çok hızlı anlatılıyor. İşlevlerin tanıtımına işe yarar bir 'find' işleviyle başlıyor ve teker teker tasarımı geliştiriyor.
O şekilde tanıtımı çok akıcı bulduğum için aktarmak istedim. Bunların arasında aralık kavramı da kendisini gösteriyor.
Bir dizide arama yapan bir işlev olsun:
bool ara(int[] dizi, int sayı)
{
foreach (eleman; dizi) {
if (sayı == eleman) {
return true; // bulundu
}
}
return false; // bulunamadı
}
void main()
{
ara([ 10, 1, 42, 5, 7 ], 42);
}
Bulunup bulunmadığını söylemek fazla kullanışlı olmadığı için bulunan elemanın yerini bildirelim. Bulunamazsa yasal olmayan bir indeks döndürür:
int ara(int[] dizi, int sayı)
{
foreach (i, eleman; dizi) {
if (sayı == eleman) {
return i;
}
}
return -1;
}
İndeks kavramı her veri yapısına uygun değildir; örneğin bağlı listenin belirli bir indeksli elemanına gitmek için baştan adım adım ilerlemek gerekeceği için çok yavaş olur.
Onun yerine erişiciler kullanılabilir (gösterge de olur, çünkü o da erişici olarak kullanılabilir). Ama Phobos aralıklara karar verdiği için yazar işte burada aralık kavramına geçiyor.
Erişici yerine, her tür veri yapısı ile çalışabilecek bir başka bir yöntem gösteriyor: Verilen diziyi başından adım adım tüketelim ve bulduğumuz noktadan geri kalanını döndürelim. Hiç bulamazsak, boş dizi dönmüş olur:
int[] ara(int[] dizi, int sayı)
{
while ((dizi.length > 0) && (dizi[0] != sayı)) {
dizi = dizi[1 .. $]; // başından tüket
}
return dizi;
}
Tabii gerçekten bir "tüketme" olmuyor. Asıl dizi olduğu gibi duruyor. Parametre olarak bir dilim geldiği için, dilimi başından tüketiyoruz. Onun etkisi, dilimi başından daraltmaktır.
Güzel... Ama o işlev kısıtlıdır, çünkü yalnızca int türüyle çalışır. Şablon haline getirerek her türle kullanabiliriz. Şablon yapmak çok kolay: işlev isminden sonra bir '(T)' ekliyoruz ve int'leri T yapıyoruz.
Her tür olabileceği için, artık "sayı" demek uygun olmaz; onu da "değer" yapalım.
int ve double kullanan bir örnek:
T[] ara(T)(T[] dizi, T değer)
{
while ((dizi.length > 0) && (dizi[0] != değer)) {
dizi = dizi[1 .. $];
}
return dizi;
}
import std.stdio;
void main()
{
writeln(ara([ 10, 1, 42, 5, 7 ], 42)); // hem int ile
writeln(ara([ 1.2, 3.4, 5.6 ], 3.4)); // hem de double ile çalışıyor
}
O işlevde yine de bir sorun var: dizi elemanlarının ve aranan değerin aynı türden olmaları gerekiyor. Örneğin double dizide int aramak istesek çalışmaz.
Onun için iki tane şablon parametresi kullanalım:
// E: dizi elemanlarının türü, A: arananın türü
E[] ara(E, A)(E[] dizi, A değer)
{
while ((dizi.length > 0) && (dizi[0] != değer)) {
dizi = dizi[1 .. $];
}
return dizi;
}
Böylece double dizi içinde int arayabiliriz:
writeln(ara([ 1.0, 3.0, 5.0 ], 3));
Onun çalışmasının nedeni, işlev içindeki '[dizi[0] != değer' işleminde solda double, sağda int bulunması, ve bunun yasal bir karşılaştırma olmasıdır.
İşlevin bu halindeki sorun da, artık fazla esnek olmasıdır. :) Karşılaştırılamyan türler bile kullanılsa, derleyici izin verir:
writeln(ara([ 1.0, 3.0, 5.0 ], "merhaba"));
Evet, bir derleme hatası olur; ama o hata, işlevin uyumsuz parametrelerle çağrıldığı yukarıdaki satıra değil, işlevin içindeki '[dizi[0] != değer' karşılaştırmasına işaret eder. İyi değil! :) Amaç, derleme hatasının uyumsuz türlerin kullanıldığı "merhaba"lı satıra işaret etmesidir.
O zaman şablon kısıtlaması (template constraint) tanımlarız:
E[] ara(E, A)(E[] dizi, A değer)
if (is (typeof(dizi[0] != değer) : bool))
{
while ((dizi.length > 0) && (dizi[0] != değer)) {
dizi = dizi[1 .. $];
}
return dizi;
}
Ne yazık ki çok karışık... :/ Oradaki şablon kısıtlaması, o şablonun ancak '[dizi[0] != değer' karşılaştırmasının sonucunun 'bool' olduğu durumlarda göze alınacağını söyler. Bir anlamda, "bu şablonun çalışabilmesi için bu koşulun gerçekleşmesi gerekir" der.
Yasal olmayan karşılaştırmaların sonucunda 'is' ifadesi 'void' bile olmayan "türsüzlük" üretir ve o da 'bool'a eşit olmaz.
Yukarıdaki kullanımda derleme hatası, artık "merhaba"lı satıra işaret eder.
Bu noktada bir de isimli şablon kısıtlaması tanımlayabiliriz. (Bu adım kitapta yok). Öyle yapınca, şablon kısıtlamasına okunaklı bir isim verilmiş olur:
E[] ara(E, A)(E[] dizi, A değer)
if (karşılaştırılabilir_mi!(E, A))
{
while ((dizi.length > 0) && (dizi[0] != değer)) {
dizi = dizi[1 .. $]; // başından tüket
}
return dizi;
}
O isimli kısıtlama şöyle tanımlanabilir:
template karşılaştırılabilir_mi(A, B)
{
const bool karşılaştırılabilir_mi = is (typeof(A.init != B.init) : bool);
}
Tabii bu işlevin birim testinin de en başından beri yazılmış olması gerekiyordu. :) O da şöyle bir şey olabilir:
unittest
{
// İlk bulunduğu noktadan gerisini vermeli
assert(ara([ 10, 1, 42, 5, 7, 42 ], 42) == [ 42, 5, 7, 42 ]);
// Bulunamazsa boş dizi dönmeli
assert(ara([ 10, 1 ], 42) == []);
// double dizide int aranabilmeli
assert(ara([ 1.0, 3.0, 5.0 ], 3) == [ 3.0, 5.0 ]);
}
Dinlediğiniz için teşekkürler... :)
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]