Bir önceki konuda (http://ddili.org/forum/thread/711) leftJustify metodunu incelemeye çalışmış ama işin göründüğü gibi basit olmadığını anlayınca tabir yerinde ise yan çizmiş ve işi yarıda bırakmıştım :-D Şimdi bunu bir kez daha denemek istiyorum. Bu sefer split() metodunun basit bir versiyonunu incelemek niyetindeyim. Bakalım neler öğreneceğiz;
S[] split(S)(S s) if (isSomeString!S)
{
size_t istart;
bool inword = false;
S[] result;
foreach (i; 0 .. s.length)
{
switch (s[i])
{
case ' ': case '\t': case '\f': case '\r': case '\n': case '\v':
if (inword)
{
result ~= s[istart .. i];
inword = false;
}
break;
default:
if (!inword)
{
istart = i;
inword = true;
}
break;
}
}
if (inword)
result ~= s[istart .. $];
return result;
}
Ben split (http://www.dlang.org/phobos/std_array.html#split) metodu olarak phobos kütüphanesinin array modülünde yaşıyorum. Beni tasarlayanlar farklı tiplerle çalışabilmem için beni bir şablon (http://ddili.org/ders/d/sablonlar.html) metodu olarak tasarladırlar. Bu sebeple normal metodlardan bazı farklarım var.
S[] split(S)(S s) if (isSomeString!S)
İşlemlerimi S isimli bir tip ile yaparım ve bu S tipini senin bildirmeni beklerim, ayrıca üzerinde çalışabilmek için senin bildirdiğin S tipinde s isimli bir değişkende isterim. Tüm işlemlerimi bitirdiktan sonra sana yine S tipinde bir dizi göndereceğim. Sende ona göre gerekli hazırlıklarını yapmalısın. Bu arada küçük bir kısıtlamam var S tipini kafana göre seçemezsin bir string tipi olmalı.
size_t istart;
bool inword = false;
S[] result;
Düzenli çalışmayı çok severim bundan dolayı öncelikle işlerim esnasında kullanacağım değişkenlerimi hazırlayayım.
foreach (i; 0 .. s.length)
Değişkenlerimde hazır olduğuna göre artık bana gönderilen karakter katarını incelemeye başlayabilirim. Ancak çalışma masam çok küçük hepsi buraya sığmaz, en güzeli ben hepsi ile teker teker ilgileneyim.
switch (s[i])
Evet şimdi ilk karakterimiz alalım bakalım kimmiş bu ?
case ' ': case '\t': case '\f': case '\r': case '\n': case '\v':
if (inword)
{
result ~= s[istart .. i];
inword = false;
}
break
"Heyy, karakter acaba sen boşluk karakteri veya bir kaçış karakteri misin?" Eğer öyle isin seninle devam edebiliriz ama öncelikle bir şeyi daha kontrol etmem gerekiyor. Eğer inword değişkenim izin verirse istart değişkenimin söylediği noktadan başlayıp senin bulunduğun moktaya kadar olan bütün karakterleri alıp result değişkenime ekleyeceğim, ama seni eklemeyeceğim. Ayrıca inword değişkenimin değerinide false olarak ayarlamalıyım. Yok inword izin vermezse seninle bir işim yok demektir sıradaki karaktere gececeğim.
default:
if (!inword)
{
istart = i;
inword = true;
}
break;
Eğer sen boşluk veya kaçış karakteri dışında başka herhangi bir karakter isen buradan devam edelim ama öncelikle inword değişkenimden izin almam gerekiyor. Eğer inword değişkenim izin verirse senin bulunduğun noktayı istart değişkenime söylemeliyim ve ardından inword değişkenimi true yapmalıyım. Yok inword izin vermezse seninle bir işim yok demektir sıradaki karaktere gececeğim.
if (inword)
result ~= s[istart .. $];
Ohh! nihayet tüm karakterleri inceleme işimi bitirdim. Ancak inword değişkenimi bir kontrol edeyim hala sonuca eklemem gereken karakterler var mı? Varsa onlarıda ekleyeyim.
return result;
ve mutlu son...boşluk ve kaçış karakterlerinden ayrılmış metin listeni al ve güle güle kullan :)
'Gözlemler :'
case ' ': case '\t': case '\f': case '\r': case '\n': case '\v':
** Bu case satırı gerçekten güzel kurgulanmış, bu sayede split metni sadece boşluklardan ayırmakla kalmamış kaçış karakterlerinide metnin içinden temizlemeyi başarmış. Burada önemli bir nokta kaçış karakterleri olmuş yani normalde bu tür karakterler kelimelerin sonunda olur ama örneğin şöyle bir kelimede "za\tfer" split \t karakterinide bir ayraç gibi görüp kelimeyi "za" ve "fer" şekilnde parçalıyor. Bu da bize çok ender de olsa kütüphane kodlarının da da bazı uç durumlarda yanlış bilgi üretebileceğini ve bu durumu kontrol etmek için elimizdeki en iyi aracın unittestler olduğunu ortaya koyuyor. Ayrıca splitin metni boşluklardan böldükten sonra boşlukları temizlediğinide gözden kaçırmamk gerekir diye düşünüyorum. Örnek uygulama şöyle olabilir.
module main;
import std.stdio;
import std.array;
void main()
{
string metin = "Za\tfer D ile çalışmayı\t seviyor.";
// split!(string)(metin); açık yazımı bu şekildedir.
string kelimeler[] = split(metin);
foreach (kelime; kelimeler)
{
writefln("Yeni kelimeler: -%s-", kelime);
}
}
** Bir diğer konu inword kullanımıdır. Bu değişken sayesinde split bir kelimenin başını ve sonunu bulabilir. Açıkcası case ve if'in bu şekilde organize edilmesi beni oldukça etkiledi. Kelimelerin başını ve sonunu bulmak için ne ölçüm yapılıyor nede bir kıyas sadece bir bayrak yardımı ile bu iş çok basitçe kotarılmış, benim hoşuma gitti.
Aslında başka dillerdeki split motodlarınıda araştırıp kıyaslar yaparak sadece D dili bilgimizi değil, kod okuma ve programcılık tekniklerimizide geliştirebiliriz diye düşünüyorum bu açıdan bakınca bu tür incelemelerin ne denli faydalı olduğununa bir kez daha inamış oldum. Ancak mümkünse iki veyda daha fazla kişi ile böyle bir çalışma çok daha verimli olur diye düşünüyoırum.
Evet, bu konuyu dahada zenginleştirmek isteyenler lütfen buyrun ;-)
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]