Bilgisayar ve Programlamaya Giriş · Ders Notu
Akış Diyagramları
Bir algoritma ilk kez kâğıda dökülürken çoğu zaman yazıdan önce bir çizim yapılır. Kutular adımları, oklar adımların sırasını gösterir. Bu çizime akış diyagramı denir. Not, akış diyagramlarında kullanılan simgeleri ve iyi bir diyagramın kurallarını tanıtır. Ardından her algoritmanın yapı taşı olan üç akış türü gelir. Sıralı akışta adımlar arka arkaya çalışır. Koşullu akışta yol bir soruya göre ayrılır. Tekrarlı akışta bir grup adım tekrar tekrar çalışır. Her yapı küçük örneklerle adım adım yürütülür; değişkenlerin her adımdaki değeri izleme tablolarında verilir. Sayaç, biriktirici ve bekçi değer gibi döngü kalıpları, iç içe döngüler, erken çıkış ve bayrak da ayrı ayrı ele alınır. Son kesimler diyagramın Java koduna nasıl çevrildiğini ve bir diyagramın hangi girdilerle test edildiğini gösterir.
Öğrenme çıktıları
- Simgeler
- Temel akış diyagramı simgelerini (başla/bitir, işlem, karar, giriş/çıkış, ok, bağlayıcı) ve ek simgeleri tanımak; her adım için doğru simgeyi seçmek.
- Akış
- Oklarla işlem sırasını kurmak; okunabilir bir diyagramın kurallarını uygulamak; aynı algoritmayı akış diyagramı ve sözde kodla anlatmak.
- Kontrol
- Sıralı, koşullu ve tekrarlı akışı diyagramda göstermek; karar zinciri, bileşik koşul, sayaç, biriktirici, bekçi değer, iç içe döngü, son koşullu döngü, erken çıkış ve bayrak kalıplarını kullanmak; bir diyagramı izleme tablosuyla adım adım yürütmek.
- Dönüşüm
- Diyagramı sözde koda ve Java programına çevirmek; büyük bir diyagramı alt süreçlere bölmek; diyagramı normal, sınır ve döngünün sıfır, bir, çok kez döndüğü girdilerle test etmek.
4.1Akış diyagramı
Bir algoritma, bir problemi çözmek için izlenecek açık ve sonlu adımlar dizisidir. Adımlar düz yazıyla da anlatılabilir. Ancak yazı uzadıkça hangi adımın hangisinden sonra geldiği, hangi durumda bir adımın atlandığı ya da tekrarlandığı gözden kaçar. Akış diyagramı bu sırayı göze görünür kılar.
4.1.1Akış diyagramı nedir?
Akış diyagramı (flowchart), bir algoritmanın adımlarını standart simgelerle, adımların çalışma sırasını da oklarla gösteren çizimdir. Simgenin biçimi adımın türünü (başlangıç, işlem, karar, giriş/çıkış), içine yazılan kısa ifade adımın ne yaptığını, ok da yürütmenin hangi adıma geçtiğini söyler.
Günlük bir örnek tanımı somutlaştırır. Bir apartman kapısında şifreli kilit vardır. Kilidin içindeki küçük bilgisayar her seferinde aynı işi yapar: tuşlanan şifreyi okur, doğru olup olmadığına bakar, doğruysa kapıyı açar, değilse bir uyarı sesi verir. Şekil 4.1 bu işi akış diyagramıyla gösterir.
Diyagram yukarıdan aşağıya okunur. Yürütme Başla yazan yuvarlak uçlu kutudan başlar. Ok, yürütmeyi “şifreyi oku” kutusuna götürür. Ardından eşkenar dörtgen biçimindeki karar kutusu gelir. İçinde bir soru vardır: “doğru mu?” Sorunun cevabına göre iki oktan biri izlenir. Evet okunu izleyen yol kapıyı açar, Hayır okunu izleyen yol uyarı verir. Her iki yol da sonunda aynı noktada buluşur ve Bitir kutusuna ulaşır.
Bu küçük çizimde akış diyagramının üç temel özelliği görülür. Birincisi, her adımın türü biçiminden anlaşılır; okuma işi ile karar işi farklı kutulardadır. İkincisi, adımların sırası oklarla bellidir; okur hangi adımın önce geldiğini tahmin etmek zorunda kalmaz. Üçüncüsü, bir karar kutusundan çıkan yollar ayrı ayrı izlenebilir; her çalıştırmada bu yollardan yalnızca biri kullanılır.
4.1.2Neden diyagram çizilir?
Akış diyagramı bir programlama dilinden bağımsızdır. Aynı diyagram Java, Python ya da C ile yazılabilir. Bu yüzden bir algoritmanın mantığı, henüz hangi dille kodlanacağı belli değilken tartışılabilir. Bir ekipte programcı olmayan kişiler de, örneğin bir laboratuvar sorumlusu ya da bir iş süreci yöneticisi, diyagramı okuyup “bu durumda ne olacak?” diye sorabilir.
Diyagram çizmenin ikinci yararı hataların erken görülmesidir. Bir karar kutusundan yalnızca bir ok çıkıyorsa öbür durumda ne olacağı düşünülmemiştir. Hiçbir yerden ok gelmeyen bir kutu hiç çalışmaz. Geri dönen bir okun yolunda hiçbir şey değişmiyorsa döngü sonsuza kadar sürer. Bu tür eksikler kodda satırlar arasına gizlenir; diyagramda ise çizimin biçiminden fark edilir.
Üçüncü yarar öğrenmeyle ilgilidir. Programlamaya yeni başlayan biri için en zor kavramlardan biri yürütmenin sırasıdır. Bilgisayar satırları tek tek ve sırayla çalıştırır. Bir koşula göre bazı satırları atlar, bazılarını tekrar eder. Akış diyagramında bu sıra parmakla izlenebilir. Not boyunca her diyagram bu yolla, kutu kutu yürütülecektir.
4.2Simgeler
Akış diyagramının simgeleri uluslararası bir standartla belirlenmiştir: ISO 5807. Standart 1985'te yayımlandı ve bugün de kullanılan çizim araçlarının çoğu ona uyar. Küçük bir algoritma için beş temel simge yeter. Diyagram büyüdüğünde ya da dosya, rapor gibi dış ortamlar gösterilmek istendiğinde ek simgelere başvurulur.
4.2.1Temel simgeler
Şekil 4.2 temel simgeleri bir arada gösterir. Her birinin görevi aşağıda tek tek açıklanır.
Başla/bitir simgesi (terminal symbol) oval ya da yuvarlak uçlu dikdörtgendir. Algoritmanın nereden başlayıp nerede bittiğini gösterir. Her diyagramda tek bir Başla ve en az bir Bitir bulunur.
Başla simgesine ok girmez, yalnızca ok çıkar. Bitir simgesine ok girer, ondan ok çıkmaz. Bir diyagramda birden fazla Bitir olabilir. Örneğin sayı tahmin oyununda doğru tahmin yapıldığında oyun hemen biter; bu durumda “Bildin” yazısından sonra ayrı bir Bitir çizmek diyagramı sadeleştirir. Yine de çoğu durumda yolları birleştirip tek bir Bitir'e götürmek okumayı kolaylaştırır.
İşlem simgesi (process symbol) dikdörtgendir. Bir hesaplamayı ya da bir atamayı (assignment) gösterir. Atama, bir değişkene yeni bir değer verilmesidir: toplam = a + b ifadesi, sağdaki a + b değerini hesaplar ve sonucu toplam adlı değişkene yazar.
Burada değişken (variable) sözcüğü ilk kez geçiyor. Değişken, bellekte adı olan bir kutu gibi düşünülebilir. Kutunun içinde her an tek bir değer bulunur. Yeni bir değer yazıldığında eskisi silinir. Atamadaki eşittir işareti matematikteki eşitlikten farklıdır. i = i + 1 matematikte anlamsızdır, çünkü hiçbir sayı kendisinin bir fazlasına eşit olamaz. Akış diyagramında ve programda ise bu ifade “i'nin şimdiki değerine 1 ekle, sonucu yeniden i'ye yaz” demektir. i önce 4 ise, bu kutu çalıştıktan sonra 5 olur.
Karar simgesi (decision symbol) eşkenar dörtgendir. İçine cevabı “evet” ya da “hayır” olan bir soru, yani bir koşul (condition) yazılır: a > b ?. Karar simgesine bir ok girer, ondan iki ok çıkar; çıkan oklar Evet ve Hayır diye etiketlenir.
Koşul bir karşılaştırmadır. Akış diyagramlarında ve Java'da altı karşılaştırma operatörü vardır. Bunlar < (küçük), <= (küçük ya da eşit), > (büyük), >= (büyük ya da eşit), == (eşit) ve != (eşit değil) operatörleridir. Eşitlik sorusu için iki eşittir işareti kullanılır. Bunun nedeni tek eşittir işaretinin atamaya ayrılmış olmasıdır. Bazı kitaplar ≤, ≥ ve ≠ işaretlerini de kullanır; anlam aynıdır.
Giriş/çıkış simgesi (input/output symbol) paralelkenardır. Algoritmanın dışarıdan veri almasını (“a değerini oku”) ya da dışarıya sonuç vermesini (“toplamı yaz”) gösterir. Dışarıdan alınan veriye girdi (input), dışarıya verilen sonuca çıktı (output) denir.
“Oku” sözcüğü verinin klavyeden, bir dosyadan ya da bir algılayıcıdan gelmesini kapsar. Akış diyagramı verinin nereden geldiğiyle ilgilenmez; yalnızca o noktada bir değerin algoritmaya girdiğini söyler. “Yaz” da aynı biçimde ekrana, yazıcıya ya da bir dosyaya yazmayı kapsar.
Akış çizgisi ya da kısaca ok (flow line), iki simgeyi birleştirir ve yürütmenin yönünü gösterir. Bağlayıcı (connector) küçük bir dairedir; birden fazla yolun birleştiği noktayı ya da diyagramın başka bir yerde sürdüğünü gösterir.
Okun ucu önemlidir. Uçsuz bir çizgi iki kutunun bağlı olduğunu söyler ama hangisinin önce çalıştığını söylemez. Bu yüzden her akış çizgisinin ucunda bir ok başı bulunur. Bağlayıcı, Şekil 4.1'de olduğu gibi bir karardan ayrılan iki yolun yeniden buluştuğu yeri belirginleştirir. İçine bir harf yazılan bağlayıcı ise aynı harfi taşıyan başka bir bağlayıcıyla eşleşir. Böylece sayfanın bir köşesinden öbür köşesine uzun bir ok çekmek gerekmez.
| Simge | Biçim | Görev | Örnek |
|---|---|---|---|
| Oval | Başlangıç ya da bitiş | Başla | |
| Dikdörtgen | İşlem (hesaplama, atama) | toplam = a + b | |
| Eşkenar dörtgen | Karar (koşul) | a > b ? | |
| Paralelkenar | Giriş / çıkış | a değerini oku | |
| Ok | Yürütme yönü | sonraki adıma geç |
4.2.2Simgenin içine ne yazılır?
Simgenin biçimi adımın türünü zaten söylediği için içine yalnızca o adımın kısa ifadesi yazılır. Karar simgesine bir soru yazılır: a > b ?. İşlem simgesine bir emir yazılır: toplam = a + b. Giriş/çıkış simgesine okunan ya da yazılan şey yazılır: “a değerini oku”, “toplamı yaz”. Bu ayrım bir diyagramı okuyan kişinin, kutunun içini okumadan önce bile o adımda ne olacağını bilmesini sağlar.
Soru biçiminin bir kuralı daha vardır: cevabı iki seçenekli olmalıdır. “a ile b'den hangisi büyük?” sorusunun cevabı “a” ya da “b” olur ama eşitlik durumunu açıkta bırakır. a > b ? sorusunun cevabı ise her zaman evet ya da hayırdır: a = b ise cevap hayırdır. Bu yüzden karar kutusuna her zaman bir karşılaştırma yazılır.
Bir algoritma, öğrencinin vize ve final notundan ortalama hesaplayıp geçme durumunu yazıyor. Adımları şunlardır: vize notunu al; final notunu al; ortalamayı vize × 0,4 + final × 0,6 olarak hesapla; ortalama 50 ya da üstü mü diye bak; sonucu ekrana yaz. Her adım hangi simgeye konur, içine ne yazılır?
Çözüm. “Vize notunu al” bir girdidir: paralelkenar, içine “vize değerini oku”. Final de aynı biçimde paralelkenara girer. Ortalama hesabı bir atamadır: dikdörtgen, içine ort = vize × 0,4 + final × 0,6. “50 ya da üstü mü” bir sorudur: eşkenar dörtgen, içine ort >= 50 ?. Sonucu yazmak bir çıktıdır: Evet ve Hayır dallarında birer paralelkenar, içlerine “Geçti” yaz ve “Kaldı” yaz. En başa Başla, en sona Bitir eklenir. Vize 40, final 60 ise ortalama 40 × 0,4 + 60 × 0,6 = 16 + 36 = 52 olur; 52 ≥ 50 olduğu için Evet yolu izlenir ve “Geçti” yazılır.
Hesaplamayı paralelkenara yazmak. toplam = a + b ekranla ya da klavyeyle ilgili değildir; bellekte bir değer hesaplar. Bu yüzden işlem simgesine, yani dikdörtgene aittir. Paralelkenar yalnızca dış dünyayla veri alışverişi içindir. Benzer bir karışıklık karar kutusuna emir yazıldığında görülür: “a'yı b ile karşılaştır ve büyüğü yaz” cümlesi bir soru değildir ve iki işi bir arada yapar. Doğrusu karar kutusuna a > b ? yazmak, yazma işlerini dallardaki paralelkenarlara taşımaktır.
4.2.3Ek simgeler
Beş temel simge küçük algoritmalar için yeterlidir. Bazı durumlarda ISO 5807'deki ek simgelere başvurulur. Büyük bir diyagram birkaç sayfaya bölünebilir, ayrı çizilmiş bir alt süreç çağrılabilir ya da bir dosya veya rapor gösterilmek istenebilir. Tablo 4.2 en sık karşılaşılan altısını özetler.
| Simge | Ad | Görev | Örnek |
|---|---|---|---|
| Sayfa dışı bağlayıcı | Diyagram başka sayfada sürer; içine sayfa ve nokta yazılır. | 2-A | |
| Önceden tanımlı işlem | Ayrıca çizilmiş bir alt süreci çağırır. | Ortalama hesapla | |
| Hazırlık | Döngü sayacının başlangıcını ve sınırını tek simgede verir. | i = 1 → n | |
| Belge | Yazdırılan çıktı ya da rapor. | Not çizelgesi | |
| Veri deposu | Diskte ya da veri tabanında saklanan veri. | Öğrenci kayıtları | |
| Açıklama | Kesikli çizgiyle bir simgeye not ekler; akışa katılmaz. | not 0–100 arası |
Sayfa dışı bağlayıcı, sayfa içindeki bağlayıcının bir benzeridir. İçindeki “2-A” etiketi “ikinci sayfadaki A noktasında devam ediyor” anlamına gelir. Önceden tanımlı işlem simgesi, kenarlarında iki dikey çizgi bulunan bir dikdörtgendir. Bu simge büyük diyagramları parçalara bölmenin aracıdır: alt süreç bir kez çizilir, ana diyagramda gerektiği her yerde bu simgeyle çağrılır. Hazırlık simgesi altıgendir ve bir sayaçlı döngünün başlangıç değerini, sınırını ve artış miktarını tek kutuda toplar. Açıklama simgesi akışa katılmaz; bir kutuya kesikli bir çizgiyle bağlanır ve okura bilgi verir. Örneğin “not 0–100 arası” açıklaması, girdinin hangi aralıkta olması gerektiğini hatırlatır.
4.3Diyagram tasarımı
Simgeleri tanımak bir diyagram çizmeye yetmez. Bir problemden diyagrama giden yolun belli adımları vardır. Bu adımlar izlendiğinde hem eksik bir durum kalmaz hem de ortaya okunması kolay bir çizim çıkar.
4.3.1Adım adım bir diyagram: iki sayının büyüğü
Problem kısadır: iki sayı veriliyor, büyük olanı yazılacak. Diyagram beş adımda kurulur.
- Girdi ve çıktıyı belirle. Algoritmanın dışarıdan ne aldığı ve dışarıya ne verdiği önce yazılır. Girdi iki sayıdır: a ve b. Çıktı büyük olan sayıdır. İkisi de paralelkenarla gösterilir: “a, b değerlerini oku” ve “büyüğü yaz”.
- Adımları sırala. Başla ve Bitir eklenir. Adımlar yukarıdan aşağıya dizilir: önce okuma, en son yazma. Okunmamış bir değer karşılaştırılamaz; bu yüzden okuma kutusu her zaman kararın üstündedir.
- Kararları ayır. Hangi sayının yazılacağı bir soruya bağlıdır: a, b'den büyük mü? Araya
a > b ?karar kutusu girer. Tek “büyüğü yaz” kutusu artık iki kutuya ayrılır: “a yaz” ve “b yaz”. - Okları bağla. Her kutu bir sonrakine okla bağlanır. Karardan çıkan iki ok Evet ve Hayır diye etiketlenir. İki dal alttaki bir bağlayıcıda birleşir ve Bitir'e gider.
- Örnekle izle. Diyagram bir girdiyle elle yürütülür. a = 4, b = 9 için 4 > 9 yanlıştır; Hayır yolu izlenir, “b yaz” kutusu çalışır ve 9 yazılır. Sonuç doğrudur.
Beşinci adım çoğu zaman atlanır ama en değerli adımdır. Elle izleme sırasında yalnız bir girdi denemek yetmez. a = 9, b = 4 ile Evet yolu da denenmelidir; bu durumda 9 yazılır. Peki a = b = 5 ise? 5 > 5 yanlış olduğu için Hayır yolu izlenir ve b, yani 5 yazılır. İki sayı eşitken hangisinin yazıldığı önemli değildir; diyagram bu durumda da doğru sonucu verir. Bu küçük deneme, karar kutusuna > yerine >= yazılsaydı da sonucun değişmeyeceğini gösterir.
4.3.2Okunabilirlik kuralları
Aynı algoritma iyi ya da kötü çizilebilir. Şekil 4.4 aynı iki sayı problemini iki biçimde gösterir. Soldaki çizimde oklar birbirini keser, karar kutusunun çıkışları etiketsizdir ve bir kutuya “b yaz, bitir, a=b” gibi üç ayrı iş sıkıştırılmıştır. Sağdaki çizim yukarıdan aşağıya akar, kararın çıkışları etiketlidir ve her kutuda tek bir iş vardır.
Kurallar üç başlıkta toplanır:
- Akışın yönü. Akış mümkün olduğunca yukarıdan aşağıya ve soldan sağa ilerler. Okur gözünü bu yönde gezdirir; ters yöne giden oklar yalnız döngülerde, geri dönüşü göstermek için kullanılır. Kesişen oklar azaltılır; kesişme kaçınılmazsa bağlayıcı kullanılır.
- Karar etiketleri. Karardan çıkan her ok Evet/Hayır (ya da Doğru/Yanlış) gibi anlamlı bir etiket taşır. Etiketsiz bir kararda hangi yolun hangi durumda seçildiği tahmine kalır.
- Kutu başına bir iş. Bir simgeye tek bir temel işlem yazılır. Bir kutuya üç işlem sıkıştırılırsa hangi işlemin önce yapıldığı belirsizleşir ve kutu bir karar içeriyorsa bu karar gözden kaçar. Diyagram büyüyüp sayfaya sığmaz hâle gelirse alt süreçlere bölünür.
Karardan yalnızca bir ok çıkarmak. Öğrenci aklındaki durumu, örneğin “not 60'tan büyükse Geçti yaz” durumunu çizer, öbür durumu unutur. Not 45 olduğunda yürütmenin nereye gideceği diyagramda yazmaz; akış kopar. Bir karar kutusundan her zaman iki ok çıkar. Öbür durumda yapılacak bir iş yoksa Hayır oku doğrudan birleşme noktasına gider.
4.3.3Akış diyagramı ve sözde kod
Algoritma anlatmanın ikinci yaygın yolu sözde koddur.
Sözde kod (pseudocode), bir algoritmanın adımlarını programlama diline benzeyen ama dilin kurallarına bağlı olmayan kısa cümlelerle yazma biçimidir. Satırlar sırayla okunur; bir kararın ya da döngünün içindeki satırlar girintiyle gösterilir.
Şekil 4.5 bir sayının mutlak değerini bulan algoritmayı iki biçimde gösterir. Sayı negatifse işareti değiştirilir, değilse olduğu gibi kalır.
oku x
eğer x < 0 ise
x = −x
yaz xİki gösterim aynı algoritmayı anlatır ve biri öbürüne satır satır çevrilebilir. “oku x” paralelkenara, “eğer x < 0 ise” karar kutusuna, girintili x = −x satırı kararın Evet dalındaki dikdörtgene, “yaz x” son paralelkenara karşılık gelir. Kararın Hayır dalında hiçbir kutu yoktur; ok doğrudan yazma kutusuna gider. Sözde kodda bu durum, “eğer” satırının altında girintili tek bir satır bulunmasıyla gösterilir.
| Akış diyagramı | Sözde kod | |
|---|---|---|
| Biçim | Simgeler ve oklar | Girintili kısa cümleler |
| Güçlü yanı | Dallanma ve döngü bir bakışta görülür | Hızlı yazılır, koda çok yakındır |
| Zayıf yanı | Büyüyünce sayfaya sığmaz, değiştirmek zahmetlidir | Akışın yapısı girintiye bağlıdır |
| Uygun olduğu yer | Öğrenme, sunum, iş süreçleri | Uzun algoritmalar, kodlamadan hemen önce |
Sözde kodun bir standardı yoktur. Kimi kitaplar “eğer … ise”, kimi “if … then” yazar; kimi atama için =, kimi ← kullanır. Önemli olan her adımın açık olmasıdır. Bu notta sözde kod Türkçe sözcüklerle ve Java'ya yakın işaretlerle yazılır: atama =, eşitlik sorusu ==, kalan %.
4.3.4Yapısal programlamanın üç yapısı
Akış diyagramında oklar istenen her yere çizilebilir. Bir okun diyagramın ortasındaki rastgele bir kutuya atlaması da teknik olarak mümkündür. 1960'larda büyüyen programlarda bu serbestlik ciddi sorunlar doğurdu. Oklar birbirine dolanıyor, bir kutuya kaç farklı yoldan gelindiği izlenemiyordu. Böyle çizimlere ve programlara, birbirine dolanmış makarnaya benzetilerek spagetti akış (spaghetti code) adı verildi.
Çözüm, okları rastgele çizmek yerine yalnızca üç kalıp kullanmaktır. Şekil 4.6 bu üç kalıbı gösterir.
Yapısal programlama (structured programming), algoritmaları yalnızca üç kontrol yapısıyla kurma yaklaşımıdır. Sıralı yapı (sequence): adımlar arka arkaya çalışır. Karar yapısı ya da seçim (selection): bir koşula göre iki yoldan biri çalışır. Döngü yapısı ya da tekrar (repetition): bir koşul doğru oldukça bir grup adım tekrar çalışır.
Her algoritma yalnızca sıra, seçim ve tekrar yapılarıyla yazılabilir. Corrado Böhm ve Giuseppe Jacopini bu sonucu 1966'da yayımladı.
Teoremin söylediği şey güçlüdür: rastgele atlayan oklara hiçbir zaman gerek yoktur. Spagetti biçimde çizilmiş her diyagramın aynı işi yapan yapısal bir karşılığı vardır. Gerekirse bir iki ek değişken (ör. bir bayrak) kullanılır.
Üç yapının ortak özelliği tek giriş, tek çıkış kuralıdır. Her yapıya yukarıdan tek bir ok girer, aşağıdan tek bir ok çıkar. Bu özellik sayesinde bir yapı, başka bir yapının içindeki herhangi bir kutunun yerine konabilir. Örneğin bir döngünün gövdesindeki dikdörtgen yerine bütün bir karar yapısı yerleştirilebilir. Buna iç içe (nested) yapılar denir. Büyük algoritmalar bu yolla, küçük yapılar iç içe geçirilerek kurulur. Diyagramın herhangi bir parçası kendi giriş ve çıkış okuyla ayrı ayrı anlaşılabilir ve test edilebilir.
Programlama dilleri de bu fikri benimsedi. Eski dillerde programın herhangi bir satırına atlayan goto deyimi yaygındı. Java'da goto ayrılmış bir sözcüktür, yani değişken adı olarak kullanılamaz, ama dilde hiçbir işe yaramaz. Java'da yürütmenin akışı yalnızca sıralı satırlarla, if/else ile ve while, do, for döngüleriyle kurulur.
4.3.5Çizim araçları
Akış diyagramı çizmek için özel bir araç şart değildir. Seçim, diyagramın ne için kullanılacağına bağlıdır:
- Kâğıt ve kalem: İlk taslak için en hızlı yoldur; silip yeniden çizmek kolaydır. Sınavlarda da diyagram elle çizilir.
- Word ve PowerPoint: Ekle → Şekiller menüsündeki Akış Çizelgesi grubunda bütün temel simgeler bulunur. Bağlayıcı oklar kutulara yapıştığı için bir kutu taşındığında ok da birlikte kayar.
- draw.io (diagrams.net): Ücretsizdir ve tarayıcıda çalışır. Hazır akış diyagramı şekilleri sunar; çizim PNG, SVG ya da PDF olarak dışa aktarılabilir.
- Microsoft Visio: Kurumsal süreç ve iş akışı diyagramları için kullanılan ticari bir araçtır.
- Flowgorithm: Diyagramı yalnızca çizmez, çalıştırır. Değişkenlerin değerini adım adım gösterir ve diyagramı Java dahil birçok dilde koda çevirir.
- Mermaid: Diyagram metinle tanımlanır, araç onu çizer. Belgelerde ve kod depolarında sık kullanılır.
Araç ne olursa olsun simgelerin anlamı ve okunabilirlik kuralları aynıdır. Dersin Akış Diyagramı Yürütücü simülatörü de Flowgorithm gibi diyagramı adım adım yürütür; bu nottaki örneklerin çoğu orada da denenebilir.
4.4Sıralı akış
Üç yapının en basiti sıralı yapıdır. Kontrol yapılarının hepsi bu temel üzerine kurulur: bir karar ya da döngü olmayan her kesim, kendi içinde sıralı akıştır.
4.4.1Sıralı yapı
Sıralı akış (sequential flow), adımların yazıldıkları sırayla, her biri bir öncekinin bitmesinden sonra ve tam bir kez çalıştığı akıştır. Dallanma ya da tekrar içermez; tek girişi ve tek çıkışı vardır.
Şekil 4.7 iki sayıyı okuyup toplamlarını yazan diyagramı gösterir. Diyagramdaki bütün oklar aşağı doğrudur; hiçbir kutudan birden fazla ok çıkmaz.
Başla
a değerini oku
b değerini oku
toplam = a + b
toplamı yaz
BitirSözde kodda Başla ile Bitir arasındaki dört satır girintilidir; her satır diyagramdaki bir kutuya karşılık gelir.
4.4.2Sıralı akışı adım adım yürütmek
Bir diyagramı yürütmek, bilgisayarın yapacağı işi elle yapmaktır. Başla kutusundan başlanır. Her adımda yalnızca bir kutu çalışır. Kutu çalıştıktan sonra değişkenlerin yeni değerleri ve varsa çıktı not edilir, sonra okun gösterdiği kutuya geçilir. Bu notlar bir tabloda toplanırsa ortaya bir izleme tablosu (trace table) çıkar. Tablo 4.4 a = 4, b = 7 girdisiyle yürütmenin izleme tablosudur.
| Adım | Çalışan kutu | a | b | toplam | Çıktı |
|---|---|---|---|---|---|
| 1 | Başla | — | — | — | |
| 2 | a değerini oku | 4 | — | — | |
| 3 | b değerini oku | 4 | 7 | — | |
| 4 | toplam = a + b | 4 | 7 | 11 | |
| 5 | toplamı yaz | 4 | 7 | 11 | 11 |
| 6 | Bitir | 4 | 7 | 11 | 11 |
Tablo sıralı akışın iki özelliğini açıkça gösterir. Birincisi, altı kutunun her biri tam bir kez çalışır; tabloda tekrar eden ya da atlanan bir satır yoktur. İkincisi, bir değişken değer aldıktan sonra değiştirilmedikçe o değeri korur: a, 2. adımda 4 olur ve sona kadar 4 kalır. Tablo okunurken her satır, “bu kutu çalıştıktan hemen sonra” durumunu anlatır.
Diyagramın Java karşılığı da aynı sırayı izler:
import java.util.Scanner;
public class Toplam {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int a = giris.nextInt(); // a değerini oku
int b = giris.nextInt(); // b değerini oku
int toplam = a + b; // toplam = a + b
System.out.println(toplam); // toplamı yaz
}
}Programın yapısı birkaç sözcükle özetlenebilir. public class Toplam satırı programın adını verir. main metodu programın başladığı yerdir; diyagramdaki Başla kutusunun karşılığı onun açılan küme parantezi, Bitir'in karşılığı kapanan küme parantezidir. Scanner, Java'nın klavyeden veri okumak için sunduğu hazır sınıftır; giris.nextInt() klavyeden bir tam sayı okur. Bu yüzden her “oku” paralelkenarı bir nextInt() çağrısına dönüşür. Atama dikdörtgeni aynen int toplam = a + b; satırına, “yaz” paralelkenarı System.out.println(toplam); satırına karşılık gelir. Satır sonlarında // ile başlayan açıklamalar vardır. Derleyici bunları yok sayar; burada her satırın diyagramdaki karşılığını hatırlatırlar.
Program 4 ve 7 girdisiyle çalıştırıldığında 11 yazar. −3 ve 10 girdisiyle 7 yazar. Sıralı bir programda girdiler değişse bile çalışan satırlar ve sıraları hiç değişmez; değişen yalnızca değerlerdir.
4.4.3Sıra neden önemlidir?
Sıralı akışta kutuların yeri keyfî değildir. Toplam kutusu okuma kutularının üstüne taşınsa a ile b'nin henüz değeri olmaz ve toplam hesaplanamaz. Bu hata kolay fark edilir. Daha sinsi hatalar, iki atamanın yer değiştirmesinden doğar.
İki değişkenin değerleri yer değiştirecek; bu işleme takas denir. a = 4, b = 7 ise işlemden sonra a = 7, b = 4 olmalıdır. Bir öğrenci diyagrama iki kutu koyar: önce a = b, sonra b = a. Sonuç ne olur? Doğru diyagram nasıl kurulur?
Çözüm. Önerilen diyagram adım adım yürütülür. a = b kutusu çalışınca a, b'nin değerini alır: a = 7, b = 7. a'nın eski değeri 4 artık hiçbir yerde yoktur. b = a kutusu çalışınca b, a'nın şimdiki değerini alır: b = 7. Sonuç a = 7, b = 7 olur; takas başarısızdır.
Sorun, ilk atamanın a'nın eski değerini silmesidir. Çözüm, bu değeri önce üçüncü bir değişkende saklamaktır. Böyle kısa süreli kullanılan değişkene geçici değişken (temporary variable) denir. Doğru sıra üç kutudur: gecici = a, a = b, b = gecici. Yürütme: gecici = 4; a = 7; b = 4. Sonuç a = 7, b = 4'tür.
import java.util.Scanner;
public class Takas {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int a = giris.nextInt();
int b = giris.nextInt();
int gecici = a; // a'nın değeri saklanır
a = b;
b = gecici;
System.out.println(a + " " + b);
}
}Program 4 7 girdisiyle çalıştırıldığında 7 4 yazar. Örnek, sıralı akışın temel kuralını gösterir: her kutu, bir öncekinin bıraktığı değerlerle çalışır. Bir atama, sağ tarafındaki değişkenin o anki değerini okur; eski ya da gelecekteki değerini değil.
Atamayı matematikteki eşitlik gibi okumak. a = b “a ile b eşittir” demek değildir, “b'nin şimdiki değerini a'ya kopyala” demektir. Kopyalamadan sonra b değişse a değişmez; a değiştirilse b değişmez. İki değişken yalnızca atama anında aynı değeri taşır.
4.5Koşullu akış
Sıralı bir algoritma her girdide aynı adımları yapar. Oysa gerçek problemlerin çoğunda yapılacak iş duruma göre değişir: not 60'ın altındaysa öğrenci kalır, şifre yanlışsa kapı açılmaz, sıcaklık sıfırın altındaysa uyarı verilir. Bu tür seçimler karar simgesiyle gösterilir.
4.5.1İki yollu karar
Koşullu akış (conditional flow), bir karar simgesinde sorulan koşulun sonucuna göre iki yoldan birinin izlendiği akıştır. Koşul doğruysa Evet yolu, yanlışsa Hayır yolu çalışır. Bu yollara dal (branch), yolun ikiye ayrılmasına dallanma (branching) denir. Dallar bir bağlayıcıda yeniden birleşir.
Şekil 4.9 bir sınav notuna göre “Geçti” ya da “Kaldı” yazan diyagramı gösterir. Geçme sınırı 60'tır; 60 ve üstü geçer.
Başla
not değerini oku
not >= 60 ?
Evet → "Geçti" yaz
Hayır → "Kaldı" yaz
BitirDiyagramda üç nokta özellikle önemlidir. Karar simgesinden iki yol çıkar ama her çalıştırmada bu yollardan yalnızca biri izlenir. “Geçti” ve “Kaldı” kutuları aynı çalıştırmada asla birlikte çalışmaz. Yollar alttaki bağlayıcıda (●) yeniden birleşir; bu nokta, karar yapısının tek çıkışıdır. Son olarak koşulun sınır değeri ayrıca önem taşır. Geçme sınırı “60 ve üstü” olduğu için koşul not >= 60 biçiminde yazılır. not > 60 yazılsaydı tam 60 alan öğrenci kalırdı.
4.5.2Kararı adım adım yürütmek
Koşullu bir diyagramı tam olarak anlamak için onu en az iki kez yürütmek gerekir. Girdilerden biri Evet yolunu, öbürü Hayır yolunu çalıştırmalıdır. Tablo 4.5 diyagramı üç girdiyle özetler: not = 72, not = 45 ve sınır değeri not = 60.
| not | Karar | İzlenen ok | Çalışan kutu | Hiç çalışmayan kutu |
|---|---|---|---|---|
| 72 | 72 >= 60 → doğru | Evet | "Geçti" yaz | Hayır dalı |
| 45 | 45 >= 60 → yanlış | Hayır | "Kaldı" yaz | Evet dalı |
| 60 | 60 >= 60 → doğru | Evet | "Geçti" yaz | Hayır dalı |
Birinci çalıştırmada kullanıcı 72 girer ve not = 72 olur. Karar kutusunda 72 >= 60 sorulur; cevap doğrudur. Akış Evet okundan devam eder ve “Geçti” yazılır. “Kaldı” kutusu bu çalıştırmada hiç çalışmaz. Yol bağlayıcıda ana akışa döner ve Bitir'e ulaşılır. İkinci çalıştırmada not = 45 olur. 45 >= 60 yanlıştır; Hayır yolu izlenir, “Kaldı” yazılır ve bu kez “Geçti” kutusu çalışmaz. İki çalıştırma birlikte diyagramdaki her kutuyu en az bir kez çalıştırmış olur.
Üçüncü satır sınır değeridir. 60 girildiğinde 60 >= 60 doğrudur ve “Geçti” yazılır. Bu satır, koşulda >= yerine yanlışlıkla > yazılmış bir diyagramı hemen ele verir: o diyagram 60 için “Kaldı” yazardı. 72 ve 45 bu hatayı yakalayamaz, çünkü iki diyagram bu girdilerde aynı sonucu verir.
Diyagramın Java karşılığı if/else deyimidir:
import java.util.Scanner;
public class GectiKaldi {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int not = giris.nextInt(); // not değerini oku
if (not >= 60) { // not >= 60 ?
System.out.println("Geçti"); // Evet dalı
} else {
System.out.println("Kaldı"); // Hayır dalı
}
}
}if sözcüğünden sonra parantez içinde koşul yazılır. Koşul doğruysa ilk küme parantezinin içi, yanlışsa else sözcüğünden sonraki küme parantezinin içi çalışır. Karar simgesi parantezdeki koşula, Evet dalı ilk bloğa, Hayır dalı else bloğuna karşılık gelir. Kapanan son küme parantezi de bağlayıcının yerini tutar: iki blok bittikten sonra yürütme aynı noktadan sürer. Program 72, 60 ve 100 için “Geçti”; 45, 59 ve 0 için “Kaldı” yazar.
4.5.3Tek yollu karar
Bazı kararlarda dallardan yalnızca birinde yapılacak iş vardır. Şekil 4.5 üzerindeki mutlak değer diyagramı buna örnektir: sayı negatifse işareti değiştirilir, negatif değilse hiçbir şey yapılmaz. Bu durumda karardan yine iki ok çıkar ama Hayır oku hiçbir kutuya uğramadan doğrudan birleşme noktasına gider. Java'da bu yapı else kısmı olmayan bir if ile yazılır:
import java.util.Scanner;
public class MutlakDeger {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int x = giris.nextInt();
if (x < 0) { // Hayır dalında kutu yok: else gerekmez
x = -x;
}
System.out.println(x);
}
}Program −5 için 5, 8 için 8, 0 için 0 yazar. Üç girdi bilinçli seçilmiştir: −5 Evet dalını, 8 Hayır dalını, 0 ise sınırı dener. 0 < 0 yanlış olduğu için 0 olduğu gibi kalır; zaten mutlak değeri de 0'dır.
Karar yapısının sık görülen bir başka örneği tek/çift sınamasıdır. Bir sayının çift olup olmadığı, 2'ye bölümünden kalana bakılarak anlaşılır. Kalan (remainder) işlemi Java'da % ile yazılır: 7 % 2 değeri 1, 12 % 2 değeri 0'dır. Karar kutusuna x % 2 == 0 ? yazılır; Evet dalı “çift”, Hayır dalı “tek” yazar.
import java.util.Scanner;
public class TekCift {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int x = giris.nextInt();
if (x % 2 == 0) { // x % 2 == 0 ?
System.out.println("çift");
} else {
System.out.println("tek");
}
}
}Tek sayıyı x % 2 == 1 ? koşuluyla sınamak. Bu koşul pozitif sayılarda doğru çalışır ama Java'da kalanın işareti bölünen sayının işaretini izler: -3 % 2 değeri 1 değil, −1'dir. Bu yüzden −3 için x % 2 == 1 yanlış çıkar ve tek bir sayı “çift” diye yazılır. x % 2 == 0 ? sorusu ise −3 için de doğru cevabı verir: -3 % 2 == 0 yanlıştır ve “tek” yazılır. Kalan sıfırla karşılaştırıldığında işaret sorunu ortadan kalkar.
4.5.4Üç sayının en büyüğü
İki sayının büyüğünü bulmak tek karar ister. Üç sayıda kararlar çoğalır ve diyagramın nasıl kurulacağı önem kazanır. Çok kullanılan bir yöntem “aday” fikridir. İlk sayı şimdilik en büyük kabul edilir. Sonra diğer sayılar tek tek bu adayla karşılaştırılır; daha büyüğü çıkarsa aday değişir.
Başla
a, b, c değerlerini oku
enb = a
b > enb ? Evet → enb = b
c > enb ? Evet → enb = c
enb değerini yaz
BitirŞekil 4.11 yöntemin diyagramıdır. İki karar arka arkaya gelir ve her birinin yalnız Evet dalında bir kutu vardır; Hayır dalları doğrudan bir sonraki adıma geçer. Yani diyagram, iki tek yollu kararın sıralı yapı içinde arka arkaya dizilmesinden oluşur. Yapıların birbirine eklenerek büyümesinin güzel bir örneğidir.
Diyagram önce (a, b, c) = (4, 9, 7), sonra (3, 2, 8) için yürütülecek. Her kararda hangi yol izlenir, enb nasıl değişir?
Çözüm. Birinci çalıştırmada üç sayı okunur ve enb = 4 olur. İlk kararda 9 > 4 doğrudur; enb = 9 olur. İkinci kararda 7 > 9 yanlıştır; enb değişmez ve 9 yazılır.
| Çalışan kutu | Karar | enb |
|---|---|---|
| enb = a | — | 4 |
| b > enb ? | 9 > 4 → Evet | 4 |
| enb = b | — | 9 |
| c > enb ? | 7 > 9 → Hayır | 9 |
| enb değerini yaz | çıktı: 9 | 9 |
İkinci çalıştırmada enb = 3 ile başlanır. 2 > 3 yanlıştır; ilk kararın Evet kutusu çalışmaz. 8 > 3 doğrudur; enb = 8 olur ve 8 yazılır.
| Çalışan kutu | Karar | enb |
|---|---|---|
| enb = a | — | 3 |
| b > enb ? | 2 > 3 → Hayır | 3 |
| c > enb ? | 8 > 3 → Evet | 3 |
| enb = c | — | 8 |
| enb değerini yaz | çıktı: 8 | 8 |
İki çalıştırma birlikte her iki kararın her iki dalını da denemiş olur. Her karar yalnızca bir kez sorulur. Sayı sayısı artsa da yöntem değişmez: on sayı için dokuz karar arka arkaya dizilir.
import java.util.Scanner;
public class EnBuyuk {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int a = giris.nextInt(), b = giris.nextInt(), c = giris.nextInt();
int enb = a; // ilk aday
if (b > enb) {
enb = b;
}
if (c > enb) {
enb = c;
}
System.out.println(enb);
}
}Program (4, 9, 7) için 9, (3, 2, 8) için 8 yazar. En büyük sayının başta olduğu (9, 4, 7) girdisinde iki karar da Hayır olur ve 9 yazılır. Üç sayı eşitse, örneğin (5, 5, 5), kararlar yine Hayır olur; enb = 5 kalır.
Adayı ilk sayı yerine sabit bir değerle, örneğin enb = 0 ile başlatmak. Girdiler pozitif oldukça bu da doğru sonuç verir; hata ancak bütün girdiler negatif olduğunda ortaya çıkar. (−2, −7, −1) için −2 > 0, −7 > 0 ve −1 > 0 yanlıştır; aday hiç değişmez ve 0 yazılır. Oysa 0 girdilerden biri bile değildir. Doğru diyagram enb = a ile başlar ve bu girdide −1 yazar.
4.5.5Çok yollu karar: karar zinciri
Bir karar kutusunun yalnızca iki çıkışı vardır. Seçenek sayısı ikiden fazlaysa kararlar bir zincir gibi dizilir: her karar bir öncekinin Hayır dalına yerleştirilir. Buna çok yollu karar (multi-way decision) denir.
Şekil 4.12 bir sıcaklık değerini dört sınıftan birine ayırır. Zincirin çalışma kuralı şudur: kararlar yukarıdan aşağıya sırayla sorulur; ilk doğru koşulun dalı çalışır ve ondan sonraki kararlar hiç sorulmaz. Hiçbir koşul doğru değilse en alttaki Hayır dalı çalışır.
| s | Sorulan kararlar | Karar sayısı | Çıktı |
|---|---|---|---|
| −5 | s < 0 ? Evet | 1 | Donma |
| 10 | s < 0 ? Hayır · s < 15 ? Evet | 2 | Soğuk |
| 15 | s < 0 ? Hayır · s < 15 ? Hayır · s < 25 ? Evet | 3 | Ilık |
| 18 | s < 0 ? Hayır · s < 15 ? Hayır · s < 25 ? Evet | 3 | Ilık |
| 30 | s < 0 ? Hayır · s < 15 ? Hayır · s < 25 ? Hayır | 3 | Sıcak |
Tablo 4.6'daki s = 18 satırı adım adım şöyle okunur. İlk karar 18 < 0 sorusudur; yanlıştır ve Hayır dalı ikinci karara gider. 18 < 15 de yanlıştır. Üçüncü karar 18 < 25 doğrudur; “Ilık” yazılır. s = −5 için ise ilk karar doğrudur ve öbür iki karar hiç sorulmaz.
Zincirdeki her karar yalnızca üst sınırı denetler. s < 15 sorusu sorulurken s'nin 0 ya da daha büyük olduğu kesindir; öyle olmasaydı yürütme bu karara hiç gelmezdi. Bu yüzden “0 ≤ s ve s < 15” gibi iki taraflı bir koşula gerek kalmaz. Sınır değerlerinin hangi sınıfa düştüğü de bu kuraldan anlaşılır: s = 15 için 15 < 15 yanlıştır, karar zincirde ilerler ve 15 “Ilık” olur. s = 0 ise “Soğuk”tur.
Kararların sırası önemlidir. Kararlar ters sırada dizilse, yani en başta s < 25 ? sorulsa, s = −5 için −5 < 25 doğru çıkar ve “Ilık” yazılır; “Donma” dalına hiç ulaşılmaz. s = 10 için de aynı şey olur. Genel kural kısadır: dar koşul önce, geniş koşul sonra sorulur. Seçenek sayısı n ise n − 1 karar gerekir; dört sınıf için üç karar vardır.
Java'da karar zinciri if … else if … else biçiminde yazılır. Her else if bir önceki koşulun Hayır dalıdır; son else hiçbir koşulun tutmadığı durumdur:
import java.util.Scanner;
public class Sicaklik {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int s = giris.nextInt();
if (s < 0) {
System.out.println("Donma");
} else if (s < 15) {
System.out.println("Soğuk");
} else if (s < 25) {
System.out.println("Ilık");
} else {
System.out.println("Sıcak");
}
}
}Program −5 için “Donma”, 0 ve 10 için “Soğuk”, 15 ve 18 için “Ilık”, 25 ve 30 için “Sıcak” yazar. Bu yedi girdi zincirin dört çıkışını ve her sınır değerini dener.
4.5.6Bileşik koşul: VE ve VEYA
Bazı kararlar iki koşulun birlikte değerlendirilmesini ister. Araç kiralayan bir şirket, müşterinin hem 18 yaşını doldurmuş olmasını hem de ehliyetinin bulunmasını şart koşar. Bir takvim uygulaması, gün cumartesi ya da pazar ise “Hafta sonu” yazar. İki koşulu birleştiren böyle ifadelere bileşik koşul (compound condition) denir.
VE ile bağlanan bileşik koşul, iki koşul da doğruysa doğrudur; biri bile yanlışsa yanlıştır. VEYA ile bağlanan bileşik koşul, koşullardan en az biri doğruysa doğrudur; ikisi de yanlışsa yanlıştır. Java'da VE &&, VEYA || operatörüyle yazılır.
Bileşik koşul tek bir karar kutusuna yazılabilir: yaş >= 18 VE ehliyet var ?. Diyagramda ayrıntı gerekiyorsa iki ayrı karara açılır. Şekil 4.13 iki bağlacın iki kararla nasıl çizildiğini gösterir.
VE yapısında ikinci karar, birincinin Evet dalına yerleştirilir. Birinci karar Hayır ise sonuç zaten Hayır'dır ve ikinci karar hiç sorulmaz. VEYA yapısında ikinci karar birincinin Hayır dalındadır. Birinci karar Evet ise sonuç zaten Evet'tir ve ikinciye gerek kalmaz. Tablo 4.7 ile Tablo 4.8 bu davranışı bütün durumlar için gösterir.
| yaş | ehliyet | yaş >= 18 ? | ehliyet var ? | Sonuç |
|---|---|---|---|---|
| 20 | var | Evet | Evet | Kiralanabilir |
| 20 | yok | Evet | Hayır | Kiralanamaz |
| 16 | var | Hayır | sorulmaz | Kiralanamaz |
| 16 | yok | Hayır | sorulmaz | Kiralanamaz |
| gün | gün == Cmt ? | gün == Paz ? | Sonuç |
|---|---|---|---|
| Cmt | Evet | sorulmaz | Hafta sonu |
| Paz | Hayır | Evet | Hafta sonu |
| Sal | Hayır | Hayır | Hafta içi |
Kısa devre değerlendirme (short-circuit evaluation), bileşik koşulun sonucu ilk koşuldan belli olduğunda ikinci koşulun hiç hesaplanmamasıdır. A && B ifadesinde A yanlışsa B'ye bakılmaz; A || B ifadesinde A doğruysa B'ye bakılmaz.
Java'daki && ve || operatörleri, Şekil 4.13'teki iki kararlı diyagramlarla aynı biçimde çalışır. Bu davranış zaman kazandırmakla kalmaz, bazı hataları da önler. sayac > 0 && toplam / sayac > 50 koşulunda sayac 0 ise ikinci kısım hiç hesaplanmaz; böylece sıfıra bölme hatası oluşmaz.
import java.util.Scanner;
public class Kiralama {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int yas = giris.nextInt();
boolean ehliyet = giris.nextBoolean(); // true: ehliyet var
String gun = giris.next(); // Pzt, Sal, ..., Cmt, Paz
if (yas >= 18 && ehliyet) {
System.out.println("Kiralanabilir");
} else {
System.out.println("Kiralanamaz");
}
if (gun.equals("Cmt") || gun.equals("Paz")) {
System.out.println("Hafta sonu");
} else {
System.out.println("Hafta içi");
}
}
}Program “20 true Cmt” girdisiyle “Kiralanabilir” ve “Hafta sonu”; “16 true Paz” girdisiyle “Kiralanamaz” ve “Hafta sonu” yazar. Bir bileşik koşulu test etmek için her kararın iki çıkışı da denenir. VE örneğinde (20, var), (20, yok) ve (16, —) girdileri üç farklı yolu çalıştırır. 16 yaşında birinin ehliyeti olup olmaması sonucu değiştirmez, çünkü ikinci karar sorulmaz.
4.5.7Artık yıl
Karar zincirinde sıranın önemini gösteren klasik örnek, artık yıl kuralıdır. Gregoryen takvimde kural üç parçalıdır: 4'e bölünen yıllar artık yıldır; ama 100'e bölünenler artık değildir; 400'e bölünenler ise yine artıktır. Artık yılda şubat 29 gün çeker.
Kuralın istisnaları vardır, istisnaların da istisnası vardır. Böyle bir kural diyagrama çevrilirken en dar koşul en başa konur. 400'e bölünme, 100'e bölünmenin; 100'e bölünme de 4'e bölünmenin özel bir durumudur. Bu yüzden kararlar 400, 100, 4 sırasıyla sorulur.
Başla
yıl değerini oku
yıl % 400 == 0 ? Evet → "Artık" yaz
yıl % 100 == 0 ? Evet → "Değil" yaz
yıl % 4 == 0 ? Evet → "Artık" yaz
Hayır → "Değil" yaz
Bitir| yıl | yıl % 400 | yıl % 100 | yıl % 4 | Sorulan karar | Çıktı |
|---|---|---|---|---|---|
| 2024 | 24 → Hayır | 24 → Hayır | 0 → Evet | 3 | Artık |
| 1900 | 300 → Hayır | 0 → Evet | sorulmaz | 2 | Değil |
| 2000 | 0 → Evet | sorulmaz | sorulmaz | 1 | Artık |
| 2023 | 23 → Hayır | 23 → Hayır | 3 → Hayır | 3 | Değil |
Tablo 4.9 dört girdinin her birinin hangi yoldan geçtiğini gösterir. 2024 için 2024 % 400 = 24 ve 2024 % 100 = 24 sıfır değildir; iki karar da Hayır'dır. 2024 % 4 = 0 olduğu için üçüncü karar Evet'tir ve “Artık” yazılır. 1900'de 1900 % 400 = 300 sıfır değildir ama 1900 % 100 = 0'dır. İkinci karar Evet olur, “Değil” yazılır ve üçüncü karar hiç sorulmaz. 1900, 4'e bölündüğü hâlde artık yıl değildir; yalnız yıl % 4 == 0 sorusuna bakan bir diyagram burada yanılırdı. 2000'de ilk karar Evet'tir: 2000, 100'e bölündüğü hâlde 400'e de bölündüğü için artık yıldır. 2023 ise üç kararın da Hayır olduğu son dalı çalıştırır.
Dört girdi, diyagramın dört çıkışının hepsini birer kez çalıştırır. Bir karar zincirini test ederken amaç tam olarak budur. Java karşılığı yine bir if … else if … else zinciridir:
import java.util.Scanner;
public class ArtikYil {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int yil = giris.nextInt();
if (yil % 400 == 0) {
System.out.println("Artık");
} else if (yil % 100 == 0) {
System.out.println("Değil");
} else if (yil % 4 == 0) {
System.out.println("Artık");
} else {
System.out.println("Değil");
}
}
}Program 2024 ve 2000 için “Artık”, 1900 ve 2023 için “Değil” yazar. Yakın geleceğin ilk istisnası 2100 yılıdır: 2100, 100'e bölünür ama 400'e bölünmez; program bu yıl için “Değil” yazar. 1600 ise 400'e bölündüğü için artık yıldır.
Zinciri geniş koşulla başlatmak. Önce yıl % 4 == 0 ? sorulup Evet dalına doğrudan “Artık” yazılırsa 1900 ve 2100 de artık yıl sayılır. 100 ve 400 kuralları yalnızca 4'e bölünen yılları ilgilendirdiği için bu kurallara hiç sıra gelmez. Zincirdeki kararlar dar koşuldan geniş koşula doğru dizilmelidir.
4.6Tekrarlı akış
Bilgisayarı güçlü kılan şey, aynı işi yorulmadan binlerce kez yapabilmesidir. Bir sınıftaki 30 öğrencinin notunu toplamak, 30 kez aynı iki adımı yapmaktır: notu oku, toplama ekle. Bu adımları diyagrama 30 kez çizmek yerine bir kez çizip geri dönen bir okla tekrar ettirmek gerekir.
4.6.1Döngü yapısı
Tekrarlı akış (repetitive flow) ya da döngü (loop), bir grup adımın bir koşul doğru oldukça tekrar tekrar çalıştığı akıştır. Tekrarlanan adımlara döngü gövdesi (loop body), gövdenin bir kez çalışmasına tur (iteration) denir. Gövdenin sonundan koşula geri dönen ok döngüyü oluşturur.
Şekil 4.16 1'den 5'e kadar sayıları yazan diyagramı gösterir. Diyagramda üç parça vardır. i = 1 kutusu sayacın ilk değerini verir. i <= 5 ? karar kutusu döngünün devam edip etmeyeceğine karar verir. Evet dalındaki iki kutu, yani “i değerini yaz” ve i = i + 1, döngü gövdesidir. Gövdenin sonundan çıkan ok sola döner ve yeniden karar kutusuna girer.
Başla
i = 1
i <= 5 ?
Evet → i değerini yaz
i = i + 1
karara dön
Hayır → BitirBu diyagramda koşul gövdeden önce sorulur. Koşul daha ilk denemede yanlışsa gövde hiç çalışmaz. Böyle döngülere ön koşullu döngü (pre-test loop) denir. Koşul yanlış olduğunda Hayır dalı döngünün dışına, Bitir'e çıkar.
Döngünün bir gün bitmesi için tekrar yolunda koşulu değiştiren bir işlem bulunmalıdır. Burada bu işlem i = i + 1'dir. i her turda büyür ve sonunda 5'i geçer. Bu kutu unutulsa i hep 1 kalır, 1 <= 5 her seferinde doğru çıkar ve diyagram durmadan 1 yazar. Böyle bitmeyen döngüye sonsuz döngü (infinite loop) denir.
4.6.2Döngüyü adım adım yürütmek
Döngüde aynı kutular tekrar tekrar çalıştığı için izleme tablosunda her tur bir satırla gösterilir. Tablo 4.10 diyagramın bütün turlarını verir.
| Tur | i (karardan önce) | Karar | Yazılan | i (turun sonunda) |
|---|---|---|---|---|
| 1 | 1 | 1 <= 5 → doğru | 1 | 2 |
| 2 | 2 | 2 <= 5 → doğru | 2 | 3 |
| 3 | 3 | 3 <= 5 → doğru | 3 | 4 |
| 4 | 4 | 4 <= 5 → doğru | 4 | 5 |
| 5 | 5 | 5 <= 5 → doğru | 5 | 6 |
| — | 6 | 6 <= 5 → yanlış | — | — |
Yürütme şöyle ilerler. Sayaç ilk değerini alır: i = 1. Karar kutusunda 1 <= 5 sorulur; doğrudur ve Evet dalına, yani döngü gövdesine girilir. 1 yazılır. i = i + 1 kutusu i'yi 2 yapar. Geri dönen ok yürütmeyi yeniden karar kutusuna götürür. 2 <= 5 de doğrudur; 2 yazılır, i = 3 olur. Aynı döngü 3, 4 ve 5 için de tekrarlanır. 5 yazıldıktan sonra i = 6 olur. Karar kutusunda 6 <= 5 sorulur; bu kez cevap yanlıştır ve Hayır dalı Bitir'e götürür.
Tablodan iki sayım çıkar. Gövde 5 kez çalıştı ama koşul 6 kez soruldu. Bu her ön koşullu döngü için geçerlidir: karar, gövdeden bir fazla çalışır, çünkü son deneme döngüyü bitiren yanlış cevaptır. Döngüden çıkıldığında i'nin değeri 5 değil 6'dır. Sayacın döngüden sonraki değeri sık sık sorulan ve sık sık yanlış cevaplanan bir sorudur.
Java'da ön koşullu döngü while ile yazılır. Parantezdeki koşul doğru oldukça küme parantezinin içi tekrar çalışır; kapanan küme parantezi geri dönen okun yerini tutar:
public class BirdenBese {
public static void main(String[] args) {
int i = 1; // sayacın ilk değeri
while (i <= 5) { // karar: i <= 5 ?
System.out.print(i + " "); // i değerini yaz
i = i + 1; // sayacı güncelle
} // geri dönen ok: karara dön
System.out.println();
}
}Program 1 2 3 4 5 yazar. System.out.print, println'den farklı olarak satır sonu eklemez; sayılar bu yüzden aynı satırda, aralarında birer boşlukla yazılır. Döngüden sonraki System.out.println() satırı yalnızca satırı bitirir.
4.6.3Sayaç ve biriktirici
Döngülerin çoğunda iki tür değişken bulunur. Biri döngünün kaçıncı turda olduğunu sayar, öbürü turdan tura biriken sonucu tutar.
Sayaç (counter), kaç tekrar yapıldığını izleyen değişkendir; her turda 1 artar. Biriktirici (accumulator), kısmi sonucu tutan değişkendir; her turda yeni değer ona eklenir (ya da onunla çarpılır). Biriktiricinin başlangıç değeri işleme göre seçilir: toplam için 0, çarpım için 1.
Başlangıç değerinin seçimi rastgele değildir. Toplama için 0 seçilir, çünkü bir sayıya 0 eklemek onu değiştirmez; ilk turdan sonra toplam, ilk sayının kendisi olur. Çarpım için aynı rol 1'indir. Çarpım biriktiricisi yanlışlıkla 0 ile başlarsa her turda 0 ile çarpılır ve sonuç hep 0 çıkar. Örneğin 1'den 5'e sayıların çarpımı 1 × 1 × 2 × 3 × 4 × 5 = 120'dir; biriktirici 0 ile başlarsa 0 × 1 × 2 × 3 × 4 × 5 = 0 bulunur.
Başla
sayac = 0, toplam = 0
sayac < 3 ?
Evet → x değerini oku
toplam = toplam + x
sayac = sayac + 1
karara dön
Hayır → toplamı yaz
BitirŞekil 4.18 üç sayı okuyup toplamını yazan diyagramdır. sayac sayaç, toplam biriktiricidir. İkisi de döngüden önce, bir kez 0 değerini alır. Tablo 4.11 girdiler 4, 7 ve 2 iken her turun sonundaki değerleri verir.
| An | x | sayac | toplam | Sonraki karar |
|---|---|---|---|---|
| başlangıç | — | 0 | 0 | 0 < 3 → doğru |
| 1. tur | 4 | 1 | 4 | 1 < 3 → doğru |
| 2. tur | 7 | 2 | 11 | 2 < 3 → doğru |
| 3. tur | 2 | 3 | 13 | 3 < 3 → yanlış |
Birinci turda x = 4 okunur. Biriktirici toplam = 0 + 4 = 4 olur, sayaç sayac = 0 + 1 = 1 olur ve ok karara döner. İkinci turda x = 7 okunur; toplam = 4 + 7 = 11, sayac = 2. Üçüncü turda x = 2 okunur; toplam = 11 + 2 = 13, sayac = 3. Karar kutusunda 3 < 3 sorulur; yanlıştır ve döngüden çıkılır. Toplam yazılır: 13. Sayaç döngünün kaç kez döndüğünü, biriktirici sonucu tuttu.
import java.util.Scanner;
public class UcSayiToplami {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int sayac = 0, toplam = 0;
while (sayac < 3) {
int x = giris.nextInt(); // x değerini oku
toplam = toplam + x; // biriktirici
sayac = sayac + 1; // sayaç
}
System.out.println(toplam);
}
}Program 4 7 2 girdisiyle 13, 10 −3 5 girdisiyle 12 yazar. Java kodunda x değişkeni döngünün içinde tanımlanmıştır. Her turda yeni bir değer okunur ve önceki tur için x'in değeri artık gerekmez.
Biriktiriciyi döngünün içinde sıfırlamak. toplam = 0 kutusu yanlışlıkla döngü gövdesinin başına çizilirse her tur, önceki turların toplamını silerek başlar. Girdiler 4, 7, 2 için tablo şöyle olur:
| Tur | x | toplam (sıfırlandıktan sonra) | toplam (eklemeden sonra) | sayac |
|---|---|---|---|---|
| 1. tur | 4 | 0 | 4 | 1 |
| 2. tur | 7 | 0 | 7 | 2 |
| 3. tur | 2 | 0 | 2 | 3 |
13 yerine 2 yazılır, yani yalnız son değer kalır. Başlangıç değeri veren kutular döngüden önce, bir kez çalışmalıdır.
4.6.41'den n'e toplam
Sayaç ve biriktiricinin birlikte kullanıldığı klasik bir örnek, 1'den n'e kadar sayıların toplamıdır. Bu kez sayaç yalnızca tur saymakla kalmaz, toplanacak sayının kendisi olur.
Başla
n değerini oku
toplam = 0, i = 1
i <= n ?
Evet → toplam = toplam + i
i = i + 1
karara dön
Hayır → toplamı yaz
BitirDiyagramda i sayaç, toplam biriktiricidir. Tablo 4.12 n = 4 için yürütmeyi gösterir. Beklenen sonuç 1 + 2 + 3 + 4 = 10'dur.
| Tur | i | Karar | toplam | i (güncellenen) |
|---|---|---|---|---|
| 1. tur | 1 | 1 <= 4 → doğru | 0 + 1 = 1 | 2 |
| 2. tur | 2 | 2 <= 4 → doğru | 1 + 2 = 3 | 3 |
| 3. tur | 3 | 3 <= 4 → doğru | 3 + 3 = 6 | 4 |
| 4. tur | 4 | 4 <= 4 → doğru | 6 + 4 = 10 | 5 |
| çıkış | 5 | 5 <= 4 → yanlış | 10 | — |
Tablonun her satırı bir turdur. İlk turda 1 <= 4 doğrudur; toplam 0 + 1 = 1 olur, i 2'ye çıkar. İkinci turda toplam 1 + 2 = 3, üçüncüde 3 + 3 = 6, dördüncüde 6 + 4 = 10 olur. i = 5 olduğunda 5 <= 4 yanlıştır ve döngüden çıkılır. Gövde n = 4 kez, karar n + 1 = 5 kez çalıştı. Ekrana 10 yazılır.
Sonuç bir formülle de denetlenebilir. 1'den n'e sayıları bir kez artan, bir kez azalan sırada alt alta yazıp sütun sütun toplamak, formülü adım adım verir. n = 4 için:
| artan | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| azalan | 4 | 3 | 2 | 1 |
| sütun toplamı | 5 | 5 | 5 | 5 |
Her sütunun toplamı n + 1 = 5'tir ve n = 4 sütun vardır. İki satırın toplamı 4 × 5 = 20 eder. Bu, aranan toplamın iki katıdır; aranan toplam 20 ÷ 2 = 10 olur. Genel formül n(n + 1) / 2'dir. Formül, diyagramın doğru çalıştığını bağımsız bir yoldan doğrular.
n = 0 girilirse ne olur? i = 1 iken 1 <= 0 sorusu ilk denemede yanlıştır. Gövde hiç çalışmaz ve 0 yazılır. Bu da doğru cevaptır: hiç sayı toplanmamıştır. Formül de 0 × 1 / 2 = 0 verir. Ön koşullu döngünün “gövde hiç çalışmayabilir” özelliği burada işe yarar.
4.6.5Sayaçlı ve bekçi değerli döngü
Şimdiye kadarki döngülerin kaç kez döneceği önceden biliniyordu: 5 kez, 3 kez, n kez. Bazı işlerde bu sayı bilinmez. Kasadaki görevli kaç ürün okutacağını önceden bilmez; ürünler bittiğinde “bitti” tuşuna basar. Bir öğretmen notları girerken not sayısını saymak yerine sona özel bir değer yazar. Böyle bir özel değere bekçi değer denir.
Sayaçlı döngü (counter-controlled loop), tekrar sayısının önceden bilindiği ve bir sayaçla denetlendiği döngüdür. Bekçi değer (sentinel value), verinin bittiğini bildiren ve gerçek veride görülemeyecek biçimde seçilen özel bir değerdir. Bekçi değerli döngü (sentinel-controlled loop), bekçi değer okunana kadar dönen döngüdür.
| Sayaçla denetlenen | Bekçi değerle denetlenen | |
|---|---|---|
| Tekrar sayısı | Önceden bilinir (n kez) | Bilinmez; özel bir değer gelince durur |
| Karar | i <= n ? | x == −1 ? (bekçi geldi mi?) |
| Örnek | Sınıftaki 30 öğrencinin notunu oku | Kasada okutulan ürünler; “bitti” tuşuna kadar |
| Java'da çoğunlukla | for | while |
Bekçi değerin seçimi veriye bağlıdır. Notlar 0 ile 100 arasındaysa −1 uygun bir bekçidir, çünkü hiçbir öğrenci −1 almaz. 0 ise uygun değildir: bir öğrencinin notu gerçekten 0 olabilir ve bu not yanlışlıkla “veri bitti” diye yorumlanır.
Başla
toplam = 0, sayac = 0
x değerini oku
x == -1 ? Evet → 7. satıra git
toplam = toplam + x
sayac = sayac + 1; 3. satıra dön
ort = toplam / sayac
ort değerini yaz
BitirŞekil 4.21 bekçi değerli döngünün iki önemli ayrıntısını gösterir. Birincisi, karar kutusu oku kutusunun hemen ardından, toplama ve saymadan önce gelir. Böylece bekçi değer okunduğunda toplama da sayaca da katılmadan döngüden çıkılır. İkincisi, geri dönen ok karar kutusuna değil, oku kutusunun önüne döner. Her turda yeni bir sayı okunmalıdır; ok karara dönseydi aynı x tekrar tekrar işlenirdi.
| Okunan x | x == −1 ? | toplam | sayac |
|---|---|---|---|
| 80 | Hayır | 80 | 1 |
| 64 | Hayır | 144 | 2 |
| 90 | Hayır | 234 | 3 |
| −1 | Evet (bekçi) | 234 | 3 |
Tablo 4.14 girdiler 80, 64, 90, −1 iken yürütmeyi verir. Her okunan not önce bekçiyle karşılaştırılır. 80, 64 ve 90 gerçek nottur; toplama eklenir ve sayılır. −1 okunduğunda karar Evet olur ve döngü biter. Ortalama 234 / 3 = 78'dir. Gövde 3 kez, karar 4 kez çalıştı. Kaç not girileceği önceden bilinmiyordu; döngüyü verinin kendisi durdurdu.
İlk girdi bekçi değerse gövde hiç çalışmaz ve sayaç 0 kalır. Bu durumda ortalama hesabı sıfıra bölmeye dönüşür. Bu yüzden bölmeden önce sayac > 0 ? diye sorulur. Java karşılığı bu denetimi de içerir:
import java.util.Scanner;
public class NotOrtalamasi {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int toplam = 0, sayac = 0;
int x = giris.nextInt(); // ilk not döngüden önce okunur
while (x != -1) { // bekçi değer geldi mi?
toplam = toplam + x;
sayac = sayac + 1;
x = giris.nextInt(); // sonraki not: geri dönen ok oku kutusuna
}
if (sayac > 0) {
double ort = (double) toplam / sayac;
System.out.println(ort);
} else {
System.out.println("Not girilmedi");
}
}
}Java kodunda ilk not döngüden önce bir kez okunur, sonraki notlar gövdenin sonunda okunur. Diyagramdaki “geri dönen ok oku kutusuna gider” ayrıntısının while ile yazılışı budur. Ortalama ondalıklı çıkabileceği için double türünde hesaplanır; (double) toplam ifadesi bölmeden önce toplamı ondalıklı sayıya çevirir. Program 80 64 90 −1 için 78.0, 55 70 −1 için 62.5, yalnızca −1 için “Not girilmedi” yazar. Java ondalıklı sayıları ekrana nokta ile yazar; metinde aynı değerler 78,0 ve 62,5 diye okunur.
Bekçi değeri işlemden sonra sorgulamak. Karar kutusu toplama ve sayma kutularının altına çizilirse −1 de veri gibi işlenir:
| Okunan x | toplam | sayac | x == −1 ? |
|---|---|---|---|
| 80 | 80 | 1 | Hayır |
| 64 | 144 | 2 | Hayır |
| 90 | 234 | 3 | Hayır |
| −1 | 233 | 4 | Evet |
Toplam 234 yerine 233, sayaç 3 yerine 4 olur ve ortalama 78 yerine 233 / 4 = 58,25 çıkar. Bekçi değerin kararı, değeri kullanan her işlemden önce gelmelidir.
4.6.6İç içe döngüler
Yapısal programlamanın tek giriş, tek çıkış kuralı bir yapıyı başka bir yapının içine koymaya izin verir. Bir kararın dalında başka bir karar bulunabilir. Bir döngünün gövdesinde ikinci bir döngü bulunabilir.
İç içe döngü (nested loop), gövdesinde başka bir döngü bulunan döngüdür. Dıştakine dış döngü (outer loop), içtekine iç döngü (inner loop) denir. Dış döngünün her turunda iç döngü baştan sona çalışır.
Çarpım tablosu iç içe döngünün en tanıdık örneğidir. Dış döngü satırları (i), iç döngü sütunları (j) dolaşır; her hücreye i × j yazılır. Şekil 4.22 3 × 3'lük tabloyu gösterir. Dış döngü 3 tur döner, iç döngü de her dış turda 3 tur döner. İç gövde toplam 3 × 3 = 9 kez çalışır.
Diyagramı küçük tutmak için 2 × 2'lik tablo çizilir. Şekil 4.23 iki döngünün birbirine nasıl yerleştiğini gösterir.
Başla
i = 1
i <= 2 ? (Hayır → Bitir)
j = 1
j <= 2 ? (Hayır → i = i + 1)
i × j yaz
j = j + 1, iç karara dön
satır sonu (println), i = i + 1, dış karara dön
BitirDış döngünün gövdesi üç parçadır: j = 1, iç döngünün tamamı ve i = i + 1. j = 1 kutusunun dış gövdenin içinde olması önemlidir. Her dış turda j yeniden 1 olur; böylece iç döngü her satıra baştan başlar.
| i | j | İç karar | İş |
|---|---|---|---|
| 1 | 1 | 1 <= 2 → doğru | 1 × 1 = 1 |
| 1 | 2 | 2 <= 2 → doğru | 1 × 2 = 2 |
| 1 | 3 | 3 <= 2 → yanlış | satır sonu |
| 2 | 1 | 1 <= 2 → doğru | 2 × 1 = 2 |
| 2 | 2 | 2 <= 2 → doğru | 2 × 2 = 4 |
| 2 | 3 | 3 <= 2 → yanlış | satır sonu |
Tablo 4.15 yürütmeyi verir. i = 1 iken dış karar 1 <= 2 doğrudur. j = 1 olur; iç karar doğrudur ve 1 × 1 = 1 yazılır. j = 2 olur; 1 × 2 = 2 yazılır. j = 3 olduğunda iç karar yanlıştır ve iç döngü biter. Satır sonu yazılır, i = 2 olur ve dış karara dönülür. İkinci dış turda j yeniden 1'den başlar ve aynı yol izlenir: 2 ve 4 yazılır. i = 3 olduğunda dış karar 3 <= 2 yanlıştır ve Bitir'e gidilir. Çıktı küçük bir çarpım tablosudur:
1 2
2 4İç gövde 2 × 2 = 4 kez çalıştı. Genel olarak dış döngü n, iç döngü m tur dönüyorsa iç gövde n × m kez çalışır. Sayılar büyüdükçe bu çarpım hızla büyür: 1000 × 1000'lik bir tabloda iç gövde bir milyon kez çalışır.
public class CarpimTablosu {
public static void main(String[] args) {
int i = 1;
while (i <= 2) { // dış karar
int j = 1; // iç sayaç her dış turda yeniden 1
while (j <= 2) { // iç karar
System.out.print(i * j + " ");
j = j + 1;
}
System.out.println(); // satır sonu
i = i + 1;
}
}
}Program iki satır yazar: 1 2 ve 2 4. Java kodunda int j = 1; satırı dış döngünün küme parantezinin içindedir. Bu satır dış döngüden önceye taşınırsa j ikinci dış turda 3'te kalır, iç karar ilk denemede yanlış çıkar ve ikinci satır boş yazılır.
Çok sayıda iç içe yapı, algoritmanın bir parçasının ayrı bir alt süreç olarak çizilmesi gerektiğini gösterebilir. Üç dört düzey iç içe geçmiş kararlar ve döngüler, diyagramı da kodu da okunmaz hâle getirir.
4.6.7Sayı tahmin oyunu
Bilgisayar 1 ile 100 arasında gizli bir sayı tutar. Oyuncu tahmin eder; bilgisayar “Daha büyük” ya da “Daha küçük” diye ipucu verir. Oyun, doğru tahmin yapılınca biter ve kaç denemede bilindiği yazılır. Döngünün kaç tur döneceğini oyuncunun tahminleri belirler; bu açıdan oyun, bekçi değerli döngünün bir türüdür.
Başla
gizli = 37, deneme = 0
tahmin değerini oku
deneme = deneme + 1
tahmin == gizli ?
Evet → "Bildin (" deneme ". deneme)" yaz, Bitir
tahmin < gizli ?
Evet → "Daha büyük" yaz
Hayır → "Daha küçük" yaz
3. satıra (oku) dön
BitirDiyagramda iki karar vardır. Birincisi tahmin == gizli ? sorusudur ve döngüyü sona erdirir. İkincisi tahmin < gizli ? sorusudur ve hangi ipucunun verileceğine karar verir. İkinci karar birincinin Hayır dalındadır; tahmin doğruysa ipucu sorusu hiç sorulmaz. İki ipucu kutusunun ardından ok yeniden oku kutusuna döner. Gizli sayı normalde rastgele seçilir; izlemenin tekrarlanabilmesi için burada 37 alınmıştır.
| tahmin | deneme | tahmin == 37 ? | tahmin < 37 ? | Çıktı |
|---|---|---|---|---|
| 50 | 1 | Hayır | Hayır | Daha küçük |
| 25 | 2 | Hayır | Evet | Daha büyük |
| 37 | 3 | Evet | sorulmaz | Bildin (3. deneme) |
Oyuncu önce 50 girer; deneme = 1 olur. 50 == 37 yanlıştır, ipucu sorusuna geçilir. 50 < 37 de yanlıştır; tahmin büyük olduğu için “Daha küçük” yazılır. İkinci tahmin 25'tir; 25 < 37 doğrudur ve “Daha büyük” yazılır. Üçüncü tahmin 37'dir; eşitlik doğru çıkar ve “Bildin (3. deneme)” yazılır.
import java.util.Scanner;
public class SayiTahmin {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int gizli = 37; // oyunda: new java.util.Random().nextInt(100) + 1
int deneme = 0;
int tahmin = giris.nextInt();
deneme = deneme + 1;
while (tahmin != gizli) { // tahmin == gizli ? Hayır dalı döngüde kalır
if (tahmin < gizli) {
System.out.println("Daha büyük");
} else {
System.out.println("Daha küçük");
}
tahmin = giris.nextInt();
deneme = deneme + 1;
}
System.out.println("Bildin (" + deneme + ". deneme)");
}
}Java programı diyagramı biraz farklı bir düzenle yazar. İlk tahmin döngüden önce okunur ve döngünün koşulu tahmin != gizli olur. Sonraki tahmin gövdenin sonunda okunur. Bu, bekçi değerli döngüdeki düzenin aynısıdır. Program 50 25 37 girdisiyle “Daha küçük”, “Daha büyük” ve “Bildin (3. deneme)”; 20 40 37 girdisiyle “Daha büyük”, “Daha küçük” ve “Bildin (3. deneme)” yazar. İlk tahminde 37 girilirse döngü gövdesi hiç çalışmaz ve “Bildin (1. deneme)” yazılır.
Oyunun iyi bir stratejisi vardır: her seferinde kalan aralığın ortasını tahmin etmek. 1–100 aralığında ilk tahmin 50 olur; ipucu “Daha küçük” ise aralık 1–49'a, “Daha büyük” ise 51–100'e iner. Her tahmin aralığı yaklaşık yarıya böler. Aralık en kötü durumda 100, 50, 25, 12, 6, 3, 1 diye küçülür; bu yüzden 7 tahmin her zaman yeter. Bunun nedeni 27 = 128'in 100'den büyük olmasıdır.
4.6.8Son koşullu döngü
Bazı işlerde döngü gövdesinin en az bir kez çalışması gerekir. Kullanıcıdan 1 ile 5 arasında bir sayı istenir; girilen sayı geçersizse yeniden sorulur. Sayı okunmadan geçerli olup olmadığı sorulamaz. Bu durumda koşulu gövdenin sonuna koymak daha doğaldır.
Son koşullu döngü (post-test loop), koşulun gövdeden sonra sorulduğu döngüdür. Gövde önce çalışır, sonra koşula bakılır; bu yüzden gövde en az bir kez çalışır. Java'da do { … } while (koşul); biçiminde yazılır.
Başla
sayı değerini oku
1 <= sayı <= 5 ?
Hayır → "Geçersiz" yaz, oku'ya dön
Evet → sayıyı yaz
BitirŞekil 4.25 diyagramında yürütme Başla'dan sonra doğrudan oku kutusuna girer; arada bir karar yoktur. Karar, okumanın ardından gelir. Geçersiz bir sayıda Hayır dalı “Geçersiz” yazar ve ok yeniden oku kutusuna döner. Bu tür kullanıma girdi doğrulama (input validation) denir.
| Tur | Okunan sayı | 1 ≤ sayı ≤ 5 ? | Çıktı |
|---|---|---|---|
| 1 | 7 | yanlış → Hayır | Geçersiz |
| 2 | 0 | yanlış → Hayır | Geçersiz |
| 3 | 3 | doğru → Evet | Geçerli: 3 |
İlk turda koşul henüz hiç sorulmadan 7 okunur. Sonra karar gelir: 1 ≤ 7 ≤ 5 mi? 7, 5'ten büyük olduğu için cevap yanlıştır; “Geçersiz” yazılır. İkinci turda 0 okunur; 0, 1'den küçük olduğu için yine geçersizdir. Üçüncü turda 3 okunur; 1 ≤ 3 ≤ 5 doğrudur ve “Geçerli: 3” yazılır. Gövde 3 kez çalıştı, koşul da 3 kez soruldu. Ön koşullu döngüden farklı olarak burada karar sayısı gövde sayısına eşittir.
Diyagramdaki 1 <= sayı <= 5 koşulu matematikteki gösterimdir. Java bu yazımı kabul etmez; iki karşılaştırma bir bileşik koşulla birleştirilir. Geçersizlik koşulu “sayı 1'den küçük VEYA 5'ten büyük” olur:
import java.util.Scanner;
public class GirdiDogrulama {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int sayi;
do {
sayi = giris.nextInt(); // gövde: önce oku
if (sayi < 1 || sayi > 5) {
System.out.println("Geçersiz");
}
} while (sayi < 1 || sayi > 5); // koşul sonda sorulur
System.out.println("Geçerli: " + sayi);
}
}Program 7 0 3 girdisiyle iki kez “Geçersiz”, sonra “Geçerli: 3” yazar. İlk girdi 4 ise gövde bir kez çalışır ve doğrudan “Geçerli: 4” yazılır. Kodda geçersizlik koşulu iki kez yazılmıştır: bir kez uyarı için if içinde, bir kez döngü koşulunda. Diyagramdaki tek karar kutusu kodda iki ayrı soruya dönüşür.
while ile do-while arasındaki farkı yalnız yazım farkı sanmak. Fark, koşul ilk denemede yanlış olduğunda ortaya çıkar. 1'den n'e toplam diyagramı son koşullu çizilseydi, n = 0 için gövde bir kez çalışır, toplam 1 olurdu; oysa doğru cevap 0'dır. Gövdenin hiç çalışmaması gerekebilecek her durumda ön koşullu döngü seçilir.
4.6.9for döngüsünün diyagramı
Sayaçlı döngülerde her zaman aynı dört parça bulunur: sayacın başlangıç değeri, koşul, gövde ve sayacın güncellenmesi. Java bu kalıp için daha kısa bir yazım sunar: for döngüsü.
for (i = 1; i <= n; i++) {
toplam = toplam + i;
}- Başlangıç (
i = 1): döngüye girerken yalnızca bir kez çalışır. - Koşul (
i <= n): her turdan önce sorulur; for, ön koşullu bir döngüdür. - Gövde: koşul doğruysa çalışır.
- Güncelleme (
i++): gövdeden sonra çalışır; ardından ok koşula döner.i++,i = i + 1'in kısa yazılışıdır.
Şekil 4.26 bir while diyagramından farksızdır. for, yalnızca 1, 2 ve 4 numaralı parçaları tek satırda toplar. Bazı kaynaklar bu üç parçayı ek simgelerden hazırlık simgesinde (altıgen, “i = 1 → n”) gösterir.
public class ForToplam {
public static void main(String[] args) {
int n = 4, toplam = 0;
for (int i = 1; i <= n; i++) { // başlangıç; koşul; güncelleme
toplam = toplam + i; // gövde
}
System.out.println(toplam);
}
}Program n = 4 için 10 yazar; while ile yazılan 1'den n'e toplam programıyla aynı sonucu verir. Hangi döngünün seçileceği okunabilirlik sorunudur. Tekrar sayısı önceden belliyse for, döngüyü veri durduruyorsa while daha açık okunur.
4.6.10Erken çıkış, bayrak ve atla
Bir dizide ilk negatif sayı aranıyor olsun: a = [4, 7, −2, 5, −8]. Aranan sayı bulunduğunda kalan turlara gerek kalmaz. Gövdenin içinden döngünün dışına giden ikinci bir ok, döngüyü hemen bitirir.
Erken çıkış (early exit), döngünün koşulu henüz yanlış olmadan gövdenin içinden döngüden çıkılmasıdır; Java'da break (çık) deyimiyle yapılır. Bayrak (flag), bir olayın olup olmadığını tutan doğru/yanlış değerli değişkendir. Atla (continue) ise döngüden çıkmaz; o turun kalanını atlayıp güncellemeye ve koşula döner.
Diyagramda dizinin elemanları a[i] ile gösterilir: a[1] dizinin ilk, a[2] ikinci elemanıdır. Dizi, aynı türden değerlerin sıralı bir listesidir; burada beş sayı vardır, yani n = 5.
| i | a[i] | i <= n ? | a[i] < 0 ? | Yapılan |
|---|---|---|---|---|
| 1 | 4 | 1 <= 5 → Evet | Hayır | i = 2 |
| 2 | 7 | 2 <= 5 → Evet | Hayır | i = 3 |
| 3 | −2 | 3 <= 5 → Evet | Evet | −2 yaz, döngüden çık |
i = 1'de 4, i = 2'de 7 negatif değildir. i = 3'te −2 bulunur ve yazılır; döngü erken çıkışla biter. 5 ve −8'e hiç bakılmaz. Dizide hiç negatif olmasaydı döngü i = 6'da koşuldan çıkar ve “Yok” yazılırdı.
Erken çıkışlı döngünün iki çıkışı vardır ve bu, yapısal programlamanın tek çıkış kuralına uymaz. Tek çıkış isteniyorsa bayrak kullanılır. bulundu = yanlış ile başlanır, aranan bulununca bulundu = doğru yapılır ve döngü koşulu i <= n VE bulundu değil ? olur. Döngü bitince bayrağa bakılarak sonuç yazılır. Java'da iki yol da kullanılır:
public class IlkNegatif {
public static void main(String[] args) {
int[] a = {4, 7, -2, 5, -8};
// 1. yol: break ile erken çıkış (iki çıkışlı döngü)
int i = 0;
while (i < a.length) {
if (a[i] < 0) {
break; // aranan bulundu: döngüden çık
}
i = i + 1;
}
if (i < a.length) {
System.out.println(a[i]);
} else {
System.out.println("Yok");
}
// 2. yol: bayrak ile tek çıkışlı döngü
boolean bulundu = false;
int k = 0;
while (k < a.length && !bulundu) {
if (a[k] < 0) {
bulundu = true;
} else {
k = k + 1;
}
}
System.out.println(bulundu ? a[k] + "" : "Yok");
}
}İki yol da −2 yazar. Java'da dizi elemanları 0'dan numaralanır: a[0] 4, a[2] −2'dir. Bu yüzden kodda sayaç 0'dan başlar ve koşul i < a.length olur; a.length dizinin eleman sayısıdır (5). Bayraklı sürümde !bulundu ifadesi “bulundu değil” anlamına gelir. Koşul k < a.length && !bulundu bir bileşik koşuldur ve kısa devre değerlendirmeyle çalışır.
Atla ise döngüyü bitirmez, yalnız o turun kalanını geçer. Örneğin dizideki negatif olmayan sayıların toplamı istenirse, negatif bir sayıya gelindiğinde toplama adımı atlanır:
public class AtlaOrnegi {
public static void main(String[] args) {
int[] a = {4, 7, -2, 5, -8};
int toplam = 0;
for (int i = 0; i < a.length; i++) {
if (a[i] < 0) {
continue; // negatifi atla, sonraki tura geç
}
toplam = toplam + a[i];
}
System.out.println(toplam);
}
}Program 4 + 7 + 5 = 16 yazar. −2 ve −8'de continue çalışır; toplama satırı atlanır, i++ çalışır ve koşula dönülür. Diyagramda atla, gövdenin ortasından güncelleme kutusuna giden bir okla gösterilir.
4.7Diyagramdan Java koduna
Akış diyagramının değeri, sonunda çalışan bir programa dönüşebilmesinden gelir. Önceki kesimlerde her yapının Java karşılığı ayrı ayrı verildi. Bu kesim çeviriyi bir kural listesi olarak toplar ve tam bir örnek üzerinde satır satır uygular.
4.7.1Simgelerin kod karşılıkları
| Diyagram öğesi | Java karşılığı |
|---|---|
| Başla / Bitir | main metodunun açılan ve kapanan küme parantezi |
| Giriş (oku) | int a = giris.nextInt(); ya da sabit bir değerle değişken tanımı |
| İşlem (atama) | Atama deyimi: toplam = toplam + i; |
| Çıkış (yaz) | System.out.println(…); |
| Karar, iki dal birleşiyor | if (koşul) { … } else { … } |
| Karar zinciri | if … else if … else |
| Karar + gövdeye dönen ok (koşul önce) | while (koşul) { … } ya da for |
| Gövde + karara dönen ok (koşul sonda) | do { … } while (koşul); |
| Gövdeden döngü dışına ok | break; |
| Önceden tanımlı işlem | Metot çağrısı: ortalama(a, b, c) |
Kuralların en önemlisi döngü çevirisidir. Bir karar kutusunun Evet dalından başlayıp yeniden aynı karara dönen bir ok varsa, o karar bir while koşuludur ve oka kadar olan kutular döngü gövdesidir. Gövdenin hangi kutularda bittiğini bulmak için geri dönen okun nereden çıktığına bakılır.
4.7.21'den n'e toplam Java'da
public static void main(String[] args) {
int n = 3;
int toplam = 0, i = 1;
while (i <= n) {
toplam = toplam + i;
i = i + 1;
}
System.out.println(toplam);
}Çeviri yukarıdan aşağıya kutu kutu yapılır:
- Başla simgesi metodun başlangıcıdır:
mainaçılır. - “n değerini oku” girdi simgesidir. Örneği kısa tutmak için girdi sabit alınmıştır:
int n = 3;. Klavyeden okumak istenirse bu satırint n = giris.nextInt();olur. - “toplam = 0, i = 1” işlem simgesi iki değişkenin başlangıç değeridir:
int toplam = 0, i = 1;. i <= n ?kararından Evet dalı iki kutuya gider ve ok yeniden karara döner. Bu bir döngüdür:while (i <= n).- Evet dalındaki iki dikdörtgen döngü gövdesidir. İkisi de küme parantezinin içine, diyagramdaki sırayla yazılır.
- Geri dönen ok, gövde bloğunun kapanan küme parantezidir. Yürütme oradan koşula döner.
- Hayır dalındaki “toplamı yaz” çıkış simgesi döngünün dışındadır:
System.out.println(toplam);. - Bitir simgesi metodun kapanışıdır.
Program n = 3 için çalıştırıldığında yürütme diyagramla adım adım aynı yolu izler. İlk koşul denemesi 1 <= 3 doğrudur; toplam 0 + 1 = 1, i = 2 olur. Ardından toplam 3 ve 6 olur. i = 4 olduğunda 4 <= 3 yanlıştır; Hayır yolu izlenir ve yürütme döngü bloğunun dışına çıkar. Ekrana 6 yazılır. Sonuç formülle de doğrulanır: 3 × 4 / 2 = 6.
Çevirinin sonunda her simgenin bir kod karşılığı vardır, her kod satırının da bir simge karşılığı. Bu birebir eşleme, diyagramın doğru çevrildiğini denetlemenin en kolay yoludur. Programda karşılığı olmayan bir simge ya da diyagramda karşılığı olmayan bir satır varsa çeviride bir şey eksik ya da fazladır.
Gövde kutularından birini döngü bloğunun dışına yazmak. while (i <= n) { toplam = toplam + i; } i = i + 1; yazılırsa sayacın artışı döngünün dışında kalır. i hiç değişmez, koşul hep doğru çıkar ve program sonsuz döngüye girer. Diyagramda Evet dalındaki her kutu, geri dönen ok çıkmadan önceki son kutuya kadar, küme parantezlerinin içindedir.
4.7.3Alt süreçle modüler diyagram
Diyagramlar büyüdükçe tek sayfaya sığmaz hâle gelir. Bir işin birkaç yerde tekrarlandığı da olur. Bu durumda işin bir kısmı ayrı bir diyagram olarak çizilir ve ana diyagramdan çağrılır.
Alt süreç (subprocess), ayrı çizilmiş ve bir adla anılan diyagramdır. Ana diyagramda önceden tanımlı işlem (predefined process) simgesiyle çağrılır. Alt süreç bitince akış ana diyagramda, çağrıldığı noktadan sürer. Java'da her alt süreç bir metot (method) olur.
Şekil 4.30'daki alt diyagram Başla yerine adı ve parametreleriyle, “Ortalama(x, y, z)” ile başlar. Bitir yerine “Döndür” ile biter. Parametreler, alt sürecin çağrıldığı yerden aldığı değerlerdir. Ana diyagram ort = Ortalama(a, b, c) kutusunda a, b ve c'nin değerlerini x, y ve z'ye gönderir. a = 70, b = 85, c = 91 için alt süreçte t = 70 + 85 + 91 = 246 hesaplanır ve 246 / 3 = 82 döndürülür. Ana diyagram bu değeri ort değişkenine yazar ve bir sonraki kutudan devam eder.
Alt sürecin iki yararı vardır. Birincisi, aynı alt süreç birçok yerden çağrılabilir ama yalnızca bir kez çizilir. Ortalama hesabında bir hata bulunursa tek bir yerde düzeltilir. İkincisi, ana diyagram kısalır ve “ne yapıldığını” anlatır; “nasıl yapıldığı” alt diyagramda kalır.
import java.util.Scanner;
public class AltSurec {
// alt süreç: Ortalama(x, y, z)
static int ortalama(int x, int y, int z) {
int t = x + y + z;
return t / 3; // Döndür t / 3
}
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int a = giris.nextInt(), b = giris.nextInt(), c = giris.nextInt();
int ort = ortalama(a, b, c); // önceden tanımlı işlem simgesi
System.out.println(ort);
}
}static int ortalama(int x, int y, int z) satırı alt diyagramın başlığıdır: metodun adı, üç parametresi ve döndürdüğü değerin türü (int). return t / 3; satırı Döndür simgesinin karşılığıdır. Program 70 85 91 girdisiyle 82 yazar.
Tam sayı bölmesini unutmak. Java'da iki tam sayının bölümü de tam sayıdır; küsurat atılır. 70, 85, 91 için toplam 246 tam olarak 3'e bölündüğü için sorun görünmez. 50, 60, 71 için toplam 181'dir; gerçek ortalama yaklaşık 60,33 iken program 60 yazar. Ondalıklı sonuç gerekiyorsa metot double döndürmeli ve bölme t / 3.0 biçiminde yazılmalıdır.
4.8Doğrulama
Bir diyagramın doğru olduğu, birkaç girdiyle elle yürütülerek sınanır. Hangi girdilerin seçileceği rastgele değildir. İyi seçilmiş az sayıda girdi, kötü seçilmiş çok sayıda girdiden daha fazla hata yakalar.
4.8.1Diyagramı test etmek
Test durumu (test case), bir girdi ile o girdi için beklenen çıktının ikilisidir. Diyagram girdiyle yürütülür ve bulunan çıktı beklenenle karşılaştırılır. Sınır değeri (boundary value), bir koşulun cevabının değiştiği noktadaki girdidir; örneğin not >= 60 için 60.
Test girdileri beş kurala göre seçilir:
- Normal bir girdi seçilir ve oklar adım adım izlenir.
- Her kararın her çıkışını çalıştıracak girdiler seçilir. Diyagramdaki her kutu en az bir kez çalışmalıdır.
- Sınır değerleri ayrıca denenir.
- Her döngü için gövdenin sıfır, bir ve çok kez çalıştığı durumlar denenir.
- Başlangıca dönmeyen ya da Bitir'e ulaşmayan yollar aranır.
Şekil 4.31 kuralların Geçti/Kaldı diyagramına uygulanışını gösterir. 72 normal girdidir ve Evet yolunu çalıştırır. 45 öbür dalı çalıştırır; iki girdiyle her kutu denenmiş olur. 60 ise sınır değeridir ve karar kutusunda >= yerine > yazılmış olsaydı hatayı ortaya çıkaran tek girdi olurdu.
Döngüler için kural 4'ün nedeni şudur: döngü hataları çoğu zaman uçlarda saklanır. Biriktiricinin başlangıç değeri yanlışsa bu en çok gövdenin hiç çalışmadığı durumda görünür. Koşuldaki bir </<= karışıklığı ise genellikle bir tur eksik ya da fazla dönmeye yol açar ve bir turluk durumda kolay fark edilir. Tablo 4.20 1'den n'e toplam diyagramı için bu üç durumu verir.
| n | Durum | Gövde kaç kez çalışır | Beklenen (n(n+1)/2) | Diyagramın çıktısı |
|---|---|---|---|---|
| 0 | sıfır tekrar | 0 | 0 | 0 |
| 1 | bir tekrar | 1 | 1 | 1 |
| 5 | çok tekrar | 5 | 15 | 15 |
Üç satırda da çıktı beklenenle aynıdır. Koşul yanlışlıkla i < n yazılmış olsaydı n = 1 için gövde hiç çalışmaz ve 0 yazılırdı; n = 5 için 1 + 2 + 3 + 4 = 10 yazılırdı. Hata üç girdinin ikisinde hemen görünür. Negatif bir n ise geçersiz girdidir. Diyagram bu durumda da 0 yazar; bunun istenen davranış olup olmadığı problemin tanımına bağlıdır.
4.8.2İzleme tablosuyla elle izleme
İzleme tablosu bu notta sık sık kullanıldı. Kalıbı kısaca şöyle özetlenebilir: her değişken bir sütun, her tur bir satırdır; karar sonucu da ayrı bir sütuna yazılır. Tablo özellikle değişkenlerin birbirine bağlı biçimde değiştiği algoritmalarda vazgeçilmezdir. Öklid algoritması bunun güzel bir örneğidir.
Öklid algoritması iki sayının en büyük ortak bölenini (EBOB, greatest common divisor) bulur. Fikri şudur: a ile b'nin ortak bölenleri, b ile a'nın b'ye bölümünden kalanın ortak bölenleriyle aynıdır. Bu yüzden (a, b) çifti (b, a % b) çiftiyle değiştirilir. Kalan her turda küçülür ve sonunda 0 olur. O anda a, aranan EBOB'dur.
| An | a | b | k | b != 0 ? |
|---|---|---|---|---|
| başlangıç | 48 | 18 | — | 18 != 0 → doğru |
| 1. tur | 18 | 12 | 12 | 12 != 0 → doğru |
| 2. tur | 12 | 6 | 6 | 6 != 0 → doğru |
| 3. tur | 6 | 0 | 0 | 0 != 0 → yanlış |
Başlangıçta a = 48, b = 18'dir ve 18 != 0 doğrudur. Birinci turda k = 48 % 18 = 12 olur (48 = 2 × 18 + 12); a = 18, b = 12 yapılır. İkinci turda k = 18 % 12 = 6; a = 12, b = 6. Üçüncü turda k = 12 % 6 = 0; a = 6, b = 0. Karar 0 != 0 yanlıştır; döngüden çıkılır ve 6 yazılır. EBOB(48, 18) = 6'dır. Denetim: 48 = 8 × 6, 18 = 3 × 6 ve 8 ile 3'ün 1'den büyük ortak böleni yoktur.
Döngü 3 tur döndü, karar 4 kez soruldu: her ön koşullu döngüde olduğu gibi karar, gövdeden bir fazla çalıştı. Tablo diyagramdaki sıranın önemini de gösterir. Gövdedeki a = b ve b = k kutuları yer değiştirseydi, yani önce b = k, sonra a = b yapılsaydı ne olurdu?
| An | a | b | k | b != 0 ? |
|---|---|---|---|---|
| başlangıç | 48 | 18 | — | 18 != 0 → doğru |
| 1. tur | 12 | 12 | 12 | 12 != 0 → doğru |
| 2. tur | 0 | 0 | 0 | 0 != 0 → yanlış |
İlk turda b = 12 olur, ardından a = b ile a da 12 olur. a, b'nin eski değerini değil yeni değerini almıştır. İkinci turda 12 % 12 = 0 bulunur ve ikisi de 0 olur. Sonuç 6 yerine 0 çıkar. Bu, Örnek 4.2'deki takas hatasının aynısıdır: bir atama, daha sonra gerekecek bir değeri silmiştir.
import java.util.Scanner;
public class Oklid {
public static void main(String[] args) {
Scanner giris = new Scanner(System.in);
int a = giris.nextInt(), b = giris.nextInt();
while (b != 0) {
int k = a % b;
a = b;
b = k;
}
System.out.println(a);
}
}Program (48, 18) için 6, (270, 192) için 6, (36, 84) için 12 yazar. a < b olsa da algoritma çalışır. (18, 48) için ilk turda 18 % 48 = 18 bulunur ve a ile b yer değiştirir; sonuç yine 6'dır.
4.8.3Sık hatalar
Öğrencilerin akış diyagramlarında en sık yaptığı hatalar birkaç kalıba indirgenebilir. Tablo 4.23 çizimle ilgili, Tablo 4.24 mantıkla ilgili hataları toplar.
| Hata | Sonuç | Düzeltme |
|---|---|---|
| Etiketsiz karar yolu | Hangi koşulun seçildiği belirsiz | Evet/Hayır yaz |
| Güncellenmeyen sayaç | Sonsuz döngü | Sayaç adımı ekle |
| Bağlantısız simge | Akış kopar | Okla bağla |
| Bir simgede çok işlem | Okunabilirlik azalır | Adımlara böl |
Şekil 4.33 en tehlikeli çizim hatasını gösterir. Soldaki diyagramda geri dönen okun yolunda i'yi değiştiren bir kutu yoktur. Koşul her seferinde aynı cevabı verir ve döngü bitmez. Bir döngü çizildiğinde ilk sorulacak soru şudur: tekrar yolundaki hangi kutu koşulu bir gün yanlış yapacak?
| Hata | Belirti | Düzeltme |
|---|---|---|
| Biriktirici döngünün içinde sıfırlanıyor | 4, 7, 2 için 13 yerine 2 yazılır: yalnız son değer kalır | toplam = 0 kutusunu döngüden önceye al |
| Geri dönen ok oku kutusunu atlıyor | Aynı x tekrar tekrar işlenir; bekçi değer hiç gelmez | Ok, oku kutusunun önüne döner |
Karar kutusunda atama: x = 0 ? | Soru mu, atama mı belirsiz | Karşılaştırma yaz: x == 0 ? |
| Bekçi değer işlemden sonra sorgulanıyor | −1 toplama katılır, sayaç bir fazla olur | Kararı toplama ve saymadan önce sor |
| Karar zincirinde geniş koşul önce | Alttaki dallar hiç çalışmaz | Dar koşuldan genişe sırala |
| Karardan tek ok çıkıyor | Diğer durumda akış kopar | İki çıkışı da çiz, Evet/Hayır yaz |
| Bölmeden önce sıfır denetimi yok | Hiç veri girilmezse 0'a bölme | sayac > 0 ? kararı ekle |
Tablodaki hataların çoğu bu notun önceki kesimlerinde izleme tablolarıyla gösterildi. Ortak noktaları şudur: hepsi diyagram en az bir uygun girdiyle elle yürütüldüğünde hemen fark edilir. Biriktirici hatası 4, 7, 2 girdisiyle, bekçi hatası 80, 64, 90, −1 girdisiyle ortaya çıkar. Zincir sırası hatasını −5, sıfır denetimi eksikliğini yalnız −1'den oluşan girdi gösterir. Bir diyagram çizildikten sonra onu birkaç iyi seçilmiş girdiyle yürütmek, bu hataların hepsine karşı en etkili önlemdir.
4.9Özet
- Akış diyagramı, bir algoritmanın adımlarını standart simgelerle, yürütme sırasını oklarla gösterir. Programlama dilinden bağımsızdır; mantığı koddan önce tartışmayı ve hataları erken görmeyi sağlar.
- Beş temel simge vardır: başla/bitir (oval), işlem (dikdörtgen: hesaplama, atama), karar (eşkenar dörtgen: Evet/Hayır sorusu), giriş/çıkış (paralelkenar: oku, yaz) ve ok. Yollar bağlayıcıda birleşir. Ek simgeler (sayfa dışı bağlayıcı, önceden tanımlı işlem, hazırlık, belge, veri deposu, açıklama) büyük diyagramlarda kullanılır.
- Karar simgesine soru, işlem simgesine emir yazılır. Atama (
=) ile eşitlik sorusu (==) farklıdır. - İyi bir diyagram yukarıdan aşağıya akar, karar çıkışlarını etiketler ve her kutuda tek iş yapar. Aynı algoritma sözde kodla da anlatılabilir; iki gösterim birbirine satır satır çevrilir.
- Böhm–Jacopini teoremine göre her algoritma yalnızca sıra, seçim ve tekrar yapılarıyla yazılabilir. Her yapının tek girişi ve tek çıkışı vardır; bu yüzden yapılar iç içe kullanılabilir.
- Sıralı akışta her kutu tam bir kez ve sırayla çalışır. Kutuların sırası sonucu değiştirir; takas için geçici değişken gerekir.
- Koşullu akışta her çalıştırmada dallardan yalnızca biri izlenir. Çok seçenekte kararlar karar zinciri olarak, dar koşuldan geniş koşula dizilir; n seçenek için n − 1 karar gerekir. Bileşik koşulda VE ve VEYA, Java'da
&&ve||, kısa devre değerlendirilir. - Tekrarlı akışta geri dönen ok döngüyü oluşturur. Ön koşullu döngüde karar, gövdeden bir fazla çalışır; tekrar yolunda koşulu değiştiren bir işlem yoksa sonsuz döngü oluşur.
- Sayaç tur sayısını, biriktirici kısmi sonucu tutar; biriktirici toplam için 0, çarpım için 1 ile ve döngüden önce başlar. Bekçi değer gerçek veride görülemeyecek bir değerdir; kararı veriyi işleyen kutulardan önce sorulur.
- İç içe döngüde dış döngünün her turunda iç döngü baştan çalışır; iç gövde n × m kez çalışır. Son koşullu döngüde (
do-while) gövde en az bir kez çalışır.for, başlangıç, koşul ve güncellemeyi tek satırda toplayan ön koşullu döngüdür. - Erken çıkış (
break) döngüye ikinci bir çıkış ekler; tek çıkış isteniyorsa bayrak kullanılır. Atla (continue) yalnız o turun kalanını geçer. - Diyagram Java'ya simge simge çevrilir: oku →
nextInt(), işlem → atama, karar →if, karar + geri dönen ok →while, yaz →println, alt süreç → metot. - Diyagram, her kararın her çıkışını çalıştıran girdilerle, sınır değerleriyle ve döngünün sıfır, bir, çok kez döndüğü durumlarla test edilir. İzleme tablosunda her değişken bir sütun, her tur bir satırdır.
4.10Kavram listesi
Terimlerin İngilizce karşılıkları ve öteki sunumlardaki kullanımları terim sözlüğünde bulunur.
- akış diyagramı flowchart
- başla/bitir simgesi terminal symbol
- işlem simgesi process symbol
- karar simgesi decision symbol
- giriş/çıkış simgesi input/output symbol
- ok flow line
- bağlayıcı connector
- sayfa dışı bağlayıcı off-page connector
- önceden tanımlı işlem predefined process
- hazırlık preparation
- atama assignment
- değişken variable
- koşul condition
- sözde kod pseudocode
- yapısal programlama structured programming
- spagetti akış spaghetti code
- sıralı akış sequential flow
- geçici değişken temporary variable
- koşullu akış conditional flow
- dal branch
- çok yollu karar multi-way decision
- bileşik koşul compound condition
- kısa devre değerlendirme short-circuit evaluation
- kalan remainder
- tekrarlı akış repetitive flow
- döngü loop
- döngü gövdesi loop body
- tur iteration
- ön koşullu döngü pre-test loop
- son koşullu döngü post-test loop
- sonsuz döngü infinite loop
- sayaç counter
- biriktirici accumulator
- sayaçlı döngü counter-controlled loop
- bekçi değer sentinel value
- bekçi değerli döngü sentinel-controlled loop
- iç içe döngü nested loop
- girdi doğrulama input validation
- erken çıkış early exit
- bayrak flag
- atla continue
- alt süreç subprocess
- metot method
- test durumu test case
- sınır değeri boundary value
- izleme tablosu trace table
- en büyük ortak bölen (EBOB) greatest common divisor
4.11Kendini Dene
Aşağıdaki adımların her biri hangi simgeyle çizilir? (a)
ort = toplam / 3(b) Sayı negatif mi? (c) Sonucu ekrana yaz. (d) Ayrı bir sayfada çizilmiş “Not ortalaması hesapla” sürecini kullan.Cevap
(a) bir atamadır: işlem simgesi, dikdörtgen. (b) cevabı evet ya da hayır olan bir sorudur: karar simgesi, eşkenar dörtgen; içine
x < 0 ?yazılır. (c) dışarıya veri verir: giriş/çıkış simgesi, paralelkenar. (d) ayrıca çizilmiş bir alt süreci çağırır: önceden tanımlı işlem simgesi, kenarlarında dikey çizgiler olan dikdörtgen.Sıralı bir diyagram a = 5 okuyor, sonra sırasıyla
b = a * 2,a = b + a,c = a + bkutularını çalıştırıp a, b ve c'yi yazıyor. Her kutudan sonra değişkenler ne olur, çıktı nedir?Cevap
b = a * 2: b = 5 × 2 = 10; a = 5.a = b + a: a = 10 + 5 = 15; a'nın eski değeri 5 artık yoktur.c = a + b: c = 15 + 10 = 25. Çıktı15 10 25'tir. c hesaplanırken a'nın yeni değeri (15) kullanılır.Sıcaklık zincirindeki kararlar ters sırada dizilmiş: önce
s < 25 ?(Evet → “Ilık”), sonras < 15 ?(Evet → “Soğuk”), sonras < 0 ?(Evet → “Donma”, Hayır → “Sıcak”). s = −5, 10, 20 ve 30 için ne yazılır? Hangi çıkışlar hiç çalışamaz?Cevap
−5, 10 ve 20 için ilk karar doğrudur ve üçü de “Ilık” yazar. 30 için ilk karar yanlıştır. Yürütme ikinci karara gelir; 30 < 15 ve 30 < 0 yanlıştır ve “Sıcak” yazılır. “Soğuk” ve “Donma” dallarına hiçbir girdiyle ulaşılamaz: ikinci karara yalnızca s ≥ 25 iken gelinir ve böyle bir s ne 15'ten ne 0'dan küçüktür. Doğru sıra dar koşuldan genişe, yani s < 0, s < 15, s < 25 sırasıdır.
i = 10 ile başlanan bir döngüde karar
i > 0 ?'dır; Evet dalında i yazılır vei = i − 3yapılır. Çıktı nedir? Gövde ve karar kaçar kez çalışır, döngü bittiğinde i kaçtır?Cevap
i sırasıyla 10, 7, 4, 1 iken koşul doğrudur; bu değerler yazılır:
10 7 4 1. 1 yazıldıktan sonra i = 1 − 3 = −2 olur ve −2 > 0 yanlıştır. Gövde 4 kez, karar 5 kez çalışır; döngüden i = −2 ile çıkılır.1'den 5'e kadar sayıların çarpımını bulan bir diyagramda biriktirici
carpim = 0ile başlatılmış; gövdecarpim = carpim * i. Diyagram ne yazar? Düzeltilmiş diyagram ne yazar?Cevap
İlk turda 0 × 1 = 0 olur ve sonraki her tur 0 ile çarptığı için sonuç 0'dır. Çarpımda etkisiz değer 1'dir;
carpim = 1ile başlanınca turlar 1, 2, 6, 24, 120 değerlerini verir ve 120 yazılır.Bekçi değerli not ortalaması diyagramına sırasıyla 70, 85, 0, −1 giriliyor. Toplam, sayaç ve ortalama ne olur? Bekçi değer olarak −1 yerine 0 seçilseydi ne olurdu?
Cevap
70, 85 ve 0 gerçek nottur: toplam 70 + 85 + 0 = 155, sayaç 3 olur. −1 okununca döngü biter. Ortalama 155 / 3 ≈ 51,67'dir. Bekçi 0 olsaydı üçüncü girdi “veri bitti” diye yorumlanırdı: toplam 155, sayaç 2 ve ortalama 77,5 çıkardı. 0 gerçek bir not olabileceği için bekçi değer olarak uygun değildir.
İç içe bir döngüde dış sayaç i 1'den 3'e, iç sayaç j ise i'den 3'e kadar dönüyor. İç gövde bir “*” yazıyor; her dış turun sonunda satır sonu yazılıyor. Çıktı nedir, toplam kaç yıldız yazılır?
Cevap
i = 1 iken j = 1, 2, 3: üç yıldız. i = 2 iken j = 2, 3: iki yıldız. i = 3 iken j = 3: bir yıldız. Çıktı alt alta
***,**,*satırlarıdır. Toplam 3 + 2 + 1 = 6 yıldız yazılır. İç döngünün başlangıcı dış sayaca bağlı olduğu için iç gövde 3 × 3 = 9 değil, 6 kez çalışır.sayac = 10 ile başlayan bir döngünün gövdesi sayacı yazıp 1 artırıyor; koşul
sayac < 5. Döngü ön koşullu (while) ve son koşullu (do-while) çizildiğinde ne yazılır?Cevap
Ön koşullu döngüde koşul gövdeden önce sorulur: 10 < 5 yanlıştır ve gövde hiç çalışmaz; hiçbir şey yazılmaz. Son koşullu döngüde gövde önce çalışır: 10 yazılır, sayac 11 olur. Sonra 11 < 5 sorulur, yanlıştır ve döngü biter. Çıktı yalnızca 10'dur. İki döngü, koşul ilk denemede yanlış olduğunda farklı davranır.
Öklid algoritması (84, 36) için yürütülüyor. Her turda a, b ve k ne olur? Döngü kaç tur döner, karar kaç kez sorulur, ne yazılır?
Cevap
Başlangıç: a = 84, b = 36; 36 != 0 doğru. 1. tur: k = 84 % 36 = 12 (84 = 2 × 36 + 12); a = 36, b = 12. 2. tur: k = 36 % 12 = 0; a = 12, b = 0. 0 != 0 yanlış; döngü biter. 2 tur, 3 karar; 12 yazılır. Denetim: 84 = 7 × 12, 36 = 3 × 12 ve 7 ile 3'ün 1'den büyük ortak böleni yoktur.
4.12İleri okuma
- C. Böhm, G. Jacopini, “Flow Diagrams, Turing Machines and Languages with Only Two Formation Rules”, Communications of the ACM, 9(5), 1966: her akış diyagramının sıra, seçim ve tekrardan oluşan yapısal bir karşılığı olduğunu gösteren özgün makale.
- E. W. Dijkstra, “Go To Statement Considered Harmful”, Communications of the ACM, 11(3), 1968: rastgele atlayan deyimlerin programları neden anlaşılmaz kıldığını tartışan, yapısal programlama tartışmasını başlatan iki sayfalık mektup.
- R. Sedgewick, K. Wayne, Computer Science: An Interdisciplinary Approach (Addison-Wesley, 2016), Kesim 1.3: koşullar ve döngüler Java'da, izleme tablolarıyla ve çok sayıda kısa programla anlatılır.
- A. B. Downey, C. Mayfield, Think Java (2. baskı, O'Reilly, 2019): koşullar, mantıksal operatörler ve döngüler programlamaya yeni başlayanlar için sade bir dille anlatılır; kitabın çevrim içi sürümü ücretsizdir.
4.13Kaynaklar
- ISO 5807:1985, Information processing — Documentation symbols and conventions for data, program and system flowcharts, program network charts and system resources charts.
- P. Deitel, H. Deitel, Java: How to Program, Early Objects, 11. baskı, Pearson, 2018.
- R. Sedgewick, K. Wayne, Computer Science: An Interdisciplinary Approach, Addison-Wesley, 2016: 1.3 Conditionals and Loops.
- Oracle, The Java Tutorials: Control Flow Statements.
- A. B. Downey, C. Mayfield, Think Java, 2. baskı, O'Reilly, 2019.
İlgili sayfalar: Akış Diyagramları sunumu · Çalışma soruları · Akış Diyagramı Yürütücü simülatörü · Terim sözlüğü · © Sercan Külcü. Tüm hakları saklıdır.