Çağrışımlı Listeler
C programcılarını biraz üzelim isterseniz! Listelerde (array) indekslerin sıfırdan başlayan ardışık tamsayılar olması gerektiğini düşünenler yanılıyor. Perl listelerinde indeks olarak alfabetik diziler hatta eksi sayılar kullanabilirsiniz. Tek yapmanız gereken indeksleri "[" ve "]" arasında değil "{" ve "}" arasında yazmanız ve listenin tümünden söz ederken %liste notasyonunu kullanmak olacaktır.Örneğin, web sitenizin aylık ziyaretçi istatistiklerini çıkaran bir programın içinde
$ziyaret{'Ocak'} = $ziyaret{'Ocak'} + 1;gibi bir deyim kullanabilirsiniz. İndeksi "Ocak" olan liste elemanının değerini bir arttırmak için bundan daha kullanışlı nasıl bir yapı olabilirdi ki?
Çağrışımlı lisstelerin yararını ve kullanımını en kolay bir örnek problemle anlatabilirim:
Diyelim ki bir basit metin dosyasında her sözcüğün kaç kez geçtiğini saymanız gerekiyor. Örneğin "bir kere bir iki, bir kere iki iki eder" gibi bir satır içeren dosya üzerinde çalıştığında, programımızbir - 3 kere - 2 iki - 3 eder - 1gibi bir çıktı üretmelidir.Bu işi yapan Perl kodu şöyle birşey olabilir:
#!/usr/local/perl while ( $satir = <> ) { @sozcukler = split (/W/, $satir); foreach $sozcuk (@sozcukler) { $sayac{$sozcuk}++; } } foreach $sozcuk ( sort( keys(%sayac) ) ) { print "$sozcuk - $sayac{$sozcuk} "; } exit;Şimdi bu programı satır satır bir gözden geçrirelim:
#!/usr/local/perlBu satırım amacını biliyor olmalısınız. Gene de hatırlatmakta yarar var: UNIX kabuk programına bu satırın ardındaki satırların /usr/local/ dizinindeki "perl" isimli program tarafından yorumlanması gerektiğini bildiriyor.
while ( $satir = <> ) {Hmmm.. bu satır biraz ilginç! "<>" dosyasından okuyabildiği sürece satır okuyacak ve her defasında okuduğu satırı "$satir" değişkenine atayacak. Bu kadarı belli de <> dosyası hangi dosya, nerede duruyor, neden bir "open" deyimiyle açılmadı?Perl ile kod yazdığınızı unutuyorsunuz herhalde! Dosyayı açmadıysanız Perl sizin için açacaktır. Açılacak dosyanın adını kodun içinde belirtmediğinize göre, bunu programı çalıştırırken komut satırından vereceksiniz demektir. Güzel di mi? Evet, "elmas operatörü" (diamond operator) denen <> operatörü, Perl kodunu çalıştırırken komut satırında vereceğiniz dosyadan okumak için kullanılır.
@sozcukler = split (/W/, $satir);Bir ilginç satır daha... Deneyimli programcılar hemen bu komutu okumayı başaracaktır: $satir değişkenindeki karakter dizisi "birşeylere göre" parçalanıp her bir parça @sozcukler listesine bir eleman olarak atanacak. İyi de bu "birşeyler" de ne? Henüz regexp'leri (regular expressions) anlatmadım. O yüzden bu "birşeyleri" "sozcük içinde yer alamayacak karakterler" olarak okuyunuz. Yani "/W/" --> "Sözcük içinde yar alamayacak karakterler".Boşluklar, virgüller, diğer noktalama işaretleri, tab karakterleri vs hepsi sözcük içinde yer alamayacak karakterler olduğu için $satır karakter dizisi mükemmel bir şekilde sözcüklere parçalanacak ve her bir parça da @sozcukler listesine birer eleman olarak yerleştirilecektir. Satırın ilk sözcüğü $sozcukler[0]'a, ikinci sözcüğü $sozcukler[1]'in içine, vs.
Bu noktada Türkçe'ye özgü karakterlerin sorun çıkaracağını ve ne yazık ki "ğ", "ş" gibi karakterlerin "sözcük içinde yer alamayacak karakter" olarak değerlendirileceğini belirtmek isterim. Bunun sorumlusu kim biliyor musunuz? Türk Standartları Enstitüsü. Uzun yıllar önce karakter kümelerinin uluslararası standartlara oturtulması toplantılarına "yurt dışı seyahat sırası gelen" ama ingilizce bilmeyen ve bilgisayar bilimlerinden anlamayan bürokratların "gezsinler ve alışveriş yapsınlar diye" gönderilmesi nedeniyle tren kaçmıştı. 5-6 yıl sonra bir fırsat daha yakalamıştık, o toplantılara da ödenek olmadığı gerekçesiyle kimse gönderilmedi. İşte şimdi bu zavallı tavırların meyvelerini topluyoruz.
Neyse..
foreach $sozcuk (@sozcukler) {@sozcukler listesinin herbir elemanını sırayla "$sozcuk" değişkenine alır ve döngüyü tekrarlar.$sayac{$sozcuk}++;%sayac çağrışımlı listesinin $sozcuk'üncü elamanını bir arttıtıyor! Örneğin $sozcuk değişkeninde "eder" varsa, %sayac listesinin "eder" indeksli elemanını bir arttıracaktır. Dİkkat ederseniz %sayac listesinin tek bir elemanından, yani "eder"inci elemanından söz ederken "$sayac" dedik (ilk karakteri "$"). Normal bir liste değil de çağrışımlı bir listeden söz ettiğimizi de indeksi "{" ve "}" karakterleri arasında yazarak belli ettik.Bir numara daha çektik aslında bu satırda...
$sozcuk değişkenine ilk kez "bir" sözcüğü geldiğinde bir arttırmaya çalışacağımız $sayac{'bir'} henüz tanımsız. Tanımsız bir değeri bir arttırmak sorun çıkarmayacak mı? Hayır, çıkarmayacak! Unutmayın Perl kullanıyoruz. Herşey programcılar için! Madem programcı bir değerin bir arttırılmasını istiyor, Perl bunu yerine getirecektir. Eğer tanımsız bir değeri bir arttırmak istiyorsa önce o değeri tanımlı kılacak, sonra ilk değer olarak sıfır atayacak sonra da bir arttıracaktır.
foreach $sozcuk ( sort( keys(%sayac) ) ) {Tüm satırlar okunduğuna ve tüm sözcüklerin kaçar kez tekrarlandığı da sayıldığına göre artık elde ettiğimiz sonuçları görüntüleyebiliriz.Şimdi %sayac çağrışımlı listesi içinde dosyamızdaki sözcük çeşidi kadar eleman var. Elemanların indeksleri de sözcüklerin kendisi. Listenin herhangi bir indeksli elemanında da o sözcüğün kaç kez geçtiğinin sayısı var.
Bir başka deyişle
$sayac{'bir'} --> 3 $sayac{'kere'} --> 2 $sayac{'iki'} --> 3 $sayac{'eder'} --> 1İndeksleri ardışık doğal tamsayılar olan bir listeyi dolaşmak kolay! İndeksin sıfırdan başladığı, bir sonrakinin de bir olduğu belli. Oysa çağrışımlı listelerde durum bu kodar kolay değil. İndeksler herhangi bir kümeye ait olmadıkları için akla gelen her değeri alabilirler. Dolu bir çağrışımlı listenin tüm elemanlarını dolaşmak istediğinizde keys( ) fonksiyonu kullanmalısınız. Bu fonksiyon parametresi olarak verilen çağrışımlı listenin tüm indeksklerini normal bir listeye doldurup geri gönderir.Yani
@indeksler = keys(%sayac);diye bir komut verirseniz, @indeksler listesi$indeksler[0] = "bir", $indeksler[1] = "kere", $indeksler[2] = "iki", $indeksler[3] = "eder"şeklinde doldurulur. Bu anahtarlar listesinin hangi sırayla doldurulacağını önceden kestiremezsiniz. Tamamen anahtar (indeks) değerlerine bağlıdır. Bu listeye makul bir sırada erişmek isterseniz ayrıca "sort( )" fonksiyonuyla sıralayabilirsiniz.keys( ) fonksiyonunun bir çağrışımlı listenin anahtarlarını toparlayıp vermesi gibi values( ) fonksiyonu da bir çağrışımlı listenin değerlerini derleyip geri gönderir.
Yukardaki örnek için
keys(%sayac) --> ("bir", "iki", "kere", "eder); values(%sayac) --> (3, 3, 2, 1)listeleri alınabilir.Çağrışımlı listelere eleman eklemek kolaydır. İndeksini ve listeye eklenmesi istediğiniz değeri
$liste{'indeks'} = $deger;benzeri bir deyimle istediğiniz zaman ekleyebilirsiniz.Eleman çıkarıp atmak içinse "delete($liste{'indeks'});" deyimini kullanmalısınız.
Bir de acaba birileri bu yazı dizisinden faydalanıyor mu demişsiniz, hemen şuna dikkat çekmek isterim ki mesela regexp konusuna girdiğinizde, bundan sadece Perl programcıları değil, JavaScript, VBScript gibi regexp'in kullanıldığı programlama dillerinde sistem geliştirenler de faydalanacaktır.
Yani demem o ki pekçok kişi yazılarınızı takip ettikleri takdirde Perl ile program yazmaya kalkmasalar bile işlerine yarayacak fikirleri, bilgileri edinecekler, belki de birtakım kalıpların dışına çıkabilme becerisini kazanacak ya da en azından 'vay canına demek böyle de bir şey bu şekilde yapılabiliyormuş' diyecektir (bunu buraya yazarlar mı orası ayrı konu ;-)
Yazmaya ve paylaşmaya devam!