Bu sayfadaki yazılar
Sayfanın metni
Sayfa 23
“çözüm bulundu”'dan başkası değil-
PROBLEMİN ÇöZül
GEÇEN ZAMAN
BAŞLANGIÇ ŞEKLİ
O kkk Ek * * tk 4 * * 4 4k ** *k 4 *
* * * * * * * * kak
KALA #CCCCCCC
4k * ** * * ** * 4 * * ** 4 *
* * * * * * * KK akk ler. Bunları “sıfır” ve “bir” durumları olarak sembolize edelim. Bir altprogram “bir” durumuyla geri döndüğü zaman o seviyede veya daha alt seviyelerde çözüm bulunmuş demektir. Daha fazla uğraşmasına gerek kalmadan aynı mesajla o da geri döner. Ta ki tümüyle geri çıkılana kadar.
Peki altprogram tümüyle geri döndüğünde (dışarıdan rekursif altprogramı çağıran diğer programa geri dönüldüğünde) bildirilen değer sıfır durumuysa ne olacak? Basitçe görülmektedir ki, böyle bir durumda yapılacak bir şey yoktur. Programımız tüm ihtimalleri denemiş, çözüm bulamamıştır. Bu, programı-
PROBLEMİN ÇöZÜüMÜü
GEÇEN ZAMAN
BAŞLANGIÇ ŞEKLİ
*5 Ek * * ** * bt * * ** * ** &* kt 4 4 4k Ok ak
SANİYE) 1
KKK Ok kk CCCCCCCE* * * * * *ttk * * * *k .* * * tk kkk
* ss. ee RR mızın yetersizliğinden çok labirentin gerçekten de çözümsüz bir iabirent olduğunu gösterir. Kullanıcıya hatası bildirilir ve program bitirilir. Görüldüğü gibi arapsaçına dönecek, çorba gibi karışacak gibi görünen ihtimaller bile dikkatle değerlendirildiğinde çözümleri bulunabiliyor. Artık bu noktada programımızın kesinlikle kusursuz çalışacağına eminiz. Bir adım daha atarak programımızın ilk satırlarını oluşturalım. i) Program X ve Y parametreleriyle çağırılması gerekiyor. ii) Geri dönerken O veya 1 durumu bildiriyor. Programımız ilk satırlarında X, Y noktasında çevresine bakıyor. Gidebileceği nokta tek bir tane ise oraya gidiyor ve çevresine bakma işleminden yeniden başlıyor. Böylece yol tek yönlüyse o yolun sonuna kadar gitmesi sağlanmış oluyor. Burada bazılarımızın dikkatini çekmiş olabilir; programın bir evvelki noktaya geri dönmemesi nasıl sağlanacak? Tek bir çözüm var: Programın geçtiği noktalara iz bırakması ve böylece aynı noktalara bir daha girmemesi. Bu konuyu daha detaylı olarak birazdan bir kere daha döneceğiz. İlk satırlara geri dönelim. Programımızın düz yolda ilerlemesi sağlanmıştı. Diyelim ki bu yol üzerinde hedefe ya da çözüme rastladı; ne yapacak? Gayet basit: Bir durumuna geçerek RETURN emriyle geri dönecek ve müjdeyi üst seviyedeki programlara verecek. Diyelim ki düz yolda giderken yolu tıkandı bu sefer ne yapacak? Bu sefer de sıfır durumuna geçerek üzüntülerini üst seviyedeki programlara bildirecek. Şimdi dördüncü ve en karışık ihtimale geliyoruz; yol çatallaşırsa ne olacak? Zaten RECURSION'i gerektiren durum da bu. Programımız N, Y koordinatlarını yığına atacak ve ihtimallerden birini seçip o yönde bir nokta ilerleyecek ve o ihtimali değerlendirmesi için kendi kendini çağıracak. Geri dönüşte X, Y koordinatlarını yığından geri alacak ve çağırdığı programın gönderdiği durum kodunu kontrol edecek. Şayet durum kodu *bir'se altprogram işi çözmüş demektir. Programımız aynı durum koduyla geri dönecektir. Durum kodu “sıfır” ise alt seviyedeki programın yolu tıkanmış demektir. Bu durumda yapılacak iş girilmiş olan çıkmaz yolun belirlenmesine gelir. Arkasından diğer ihtimallerin değerlendirilmesi programın başındaki “çevresine bakma' işlemine gider. Şimdi geçtiği yola iz bırakma olayına geri dönelim. Başta basit görürakılan iz şu işlere yarayacak. Öncelikle üzerinden geçilen noktaların hazırlanması için gerekli. Hedefi bulma işi bittiğinde ise bırakılmış olan izler çözüm yolunu içerir. Fakat çözüm için gerekli olandan fazlası yani çıkmaz yolları da içerir. Bu çıkmaz yolların esas çözüm yolundan ayırdedilebilir özellikte olması gerekir. O yüzdendir ki sıfır koduyla geri dönen altprogramın bıraktığı izler değiştirilmekte (bir bakıma silinmekte). Bu safha da dikkati çeken bir nokta da her seviyede yürüyen programın bıraktığı iz farklı olursa bir kolaylık sağlanacağı. Böylece değişik seviyelerin izleri daha kolay bulunup drüzeltilebilir. Basit bir mantıksal çıkarım gösterecektir ki çözüm bulunduğu zaman her seviyeye ait bir tek iz olacaktır; bu da çözüme giden izdir. Bunca tartışmadan sonra akla gelen en basit çözüm iz bırakma kodunun seviye (veya yığın göstergeci) ile doğru orantılı veya aynı değerde olmasıdır. Herkesin olayı rahatça takip edebil-
Klavyeyle gezinmek için ← ve → tuşları.
Sayfa 22
FRMEVL o ana kadarki bulduğu değeri yığına atar ve sağ taraftaki argümanı hesaplaması için kendi kendini çağırır. Gelen sonuçla yığından geri aldığı ilk sonuç arasında daha düşük öncelikli işlemi yapar. Böylece şunlar sağlanır: Satır soldan sağa işlenir. (Zaten başka türlü bir çözüm saçma ve karışık olurdu.) Ayrıca dilendiği kadar işlem belirli öncelik sırasıyla oluşturulur ve gerçekten hatasız olarak bu sırada işlenmesi gerçekleşir. * Grafik ekranında bir resmimiz olsun. Boş olan bir bölümünü boya döker gibi doldurmak istiyoruz. Ne yapmalıyız? ÇÖZÜM: Burada gene RECURSI-ON imdadımıza yetişiyor. Bir altprogram yazarız. Yaptığı iş belli bir koordinatta çevresine bakar ve uygun yerleri doldurarak yoluna devam eder, şayet herhangi bir yerde yolu birkaç ihtimale çıkarsa o koordinatı yığına atar, ihtimallerden birini seçer, kendi kendini çağırarak o yönde doldurma işleminin devamını sağlar. Böyle bir grafik programının çalışmasını gördüyseniz bu özelliği de görmüşsünüzdür. İhtimaller birçok yerde dallanır budaklanır, program ise ihtimalfasında bir ışık yakmış ve ufkunu genişletmiş olmasıdır. Başta da dediğim gibi, konuya sakin bir şekilde paniğe kapılmadan yaklaşılırsa gerçekten de çözüm bulunabiliyor. Ben şimdi bu örneklere bir dördüncüsünü ekleyeceğim. Fakat bu örnek, diğer örnekler gibi kalmayacak. Bu örneği aynı zamanda hayata kavuşturacağız. Heyecanlanan genç programcı arkadaşlar isterlerse burada kısa bir ara verip, zihinlerini toplayıp tekrar yazının başına oturabilirler. Örneğimiz fare ve labirentle yapılan denemeler üzerine. Yalnız bizim faremiz koku alamıyor. Yollara girip çıkıp deneme-yanılma yöntemiyle yolu bulacak. Fakat daha evvel girdiği yollara bir daha girmeyecek. Yoksa bir bölgenin etrafında dönüp durmasını kimse engelleyemez. İsterseniz fareyi bir kenara bırakıp, olaya daha sade bakalım. Olay bazı gazetelerin bulmaca köşelerinde ver-
PROBLEMİN ÇöZüMü
GEÇEN ZAMAN <SAAT,DAK BAŞLANGIÇ ŞEKLİ
Ok *S * * * * 4 * * * * * * * OKKA O kak
İKA,SANIYE):
ÇÖZÜLMÜŞ ŞEKLİ
*SCCCCCCCCC 4 *CCCCECCECC* * lerden birini seçerek yoluna devam eder. O anlık öteki ihtimali (veya ihtimalleri) bırakır. Fakat eninde sonunda o noktaya geri döner ve işini kusursuz olarak bitirir. Programın bunca fazla ihtimale karşı yapacağı işi unutmamasını kullanılmasıdır. Umudumuz yukarıdaki üç örneğin birçok bilgisayar programcısının kadikleri problemler gibi bir labirent içinde bir noktadan başka bir noktaya gitme olayından başka bir şey değil. Çalışmamızı öncelikle programın yalnızca rekursif altprogram bölümü üzerinde yoğunlaştıralım. Geri kalan iş süsleme, ekran hazırlama tipinde olacağı için onları ayrı tutalım. Onları nasıl olsa yaparız. Bizi esas uğraştıracak olan rekursif altprogramın dizaynıdır. Şimdi programımızı kaba hatlarından başlayıp ince detaylara doğru adım adım gerçekleştirelim. Programımız rekursif olacak. Peki ne zaman kendi kendini çağıracak? Tabii ki yol birkaç ihtimalli olduğunda koordinatları yığında saklayıp ihtimallerden birini seçip o yönde işi min devam etmesi için kendi kendir çağıracak. Bundan başka altprogramımızın düz yolları kendi başına halletmesi ayrıca tercih edilen bir unsur. Peki... Yol tıkanınca ne olacak? Bu sorunun cevabı da oldukça basit: Yolun tıkanması kaçınılmaz bir olay. Yol tıkanınca yeni ihtimaller denenmeli. Yani yolun tıkandığı bir üst seviyedeki programa bildirilmeli ve yeni bir ihtimali seçmesi sağlanmalı. Zaten altprogramın işlemesi birkaç derinlikten sonra ele alındığında bir altprogramın bildirebileceği olaylar iki ihtimallidir. Bunlar “yol ve