assert İfadesi Çözümleri
- Bu programı
6:9ve1:2vererek çalıştırdığınızda hata atmadığını göreceksiniz. Buna rağmen, sonucun doğru olmadığını da farkedebilirsiniz:9:6'da başlayan ve 1 saat 2 dakika süren işlem, 10:8'de sonlanır
- Programa
6:9ve15:2verildiğinde atılan hata, bizi şu satıra götürür: - Bu seferki hata,
daEkiişlevindekiassertile ilgili: - Daha önce de karşılaştığımız
assertyine doğru çıkmıyor: - Burada sorun, son hanenin 0 olmasından kaynaklanıyor. Son hane sıfır olunca onlar hanesini de katarak "on", "kırk", "elli", vs. diye okuyunca 0'a verilmiş olan "da" eki her durumda doğru çalışmıyor. Bu problemin çözümünü size bırakıyorum...
Görüldüğü gibi, 6:9 girildiği halde, çıkışa 9:6 yazdırılmaktadır. Bu hata, bir sonraki problemde bir assert ifadesi yardımıyla yakalanacak.
dstring zamanDizgisi(in int saat, in int dakika) { assert((saat >= 0) && (saat <= 23)); // ...
Saat bilgisinin 0 ile 23 arasında bir değerde olmasını denetleyen bu assert ifadesinin yanlış çıkması, ancak bu işlev programın başka yerinden yanlış saat değeriyle çağrıldığında mümkündür.
zamanDizgisi işlevinin çağrıldığı sonucuYazdır işlevine baktığımızda bir yanlışlık göremiyoruz:
void sonucuYazdır( in int başlangıçSaati, in int başlangıçDakikası, in int işlemSaati, in int işlemDakikası, in int bitişSaati, in int bitişDakikası) { writef("%s'%s başlayan", zamanDizgisi(başlangıçSaati, başlangıçDakikası), daEki(başlangıçDakikası)); writef(" ve %s saat %s dakika süren işlem,", işlemSaati, işlemDakikası); writef(" %s'%s sonlanır", zamanDizgisi(bitişSaati, bitişDakikası), daEki(bitişDakikası)); writeln(); }
Bu durumda sonucuYazdır işlevini çağıran noktalardan şüphelenir ve onun programda main içinden ve tek bir noktadan çağrıldığını görürüz:
void main() { // ... sonucuYazdır(başlangıçSaati, başlangıçDakikası, işlemSaati, işlemDakikası, bitişSaati, bitişDakikası); }
Çağran noktada da bir sorun yok gibi görünüyor. Biraz daha dikkat ve zaman harcayarak sonunda başlangıç zamanının ters sırada okunduğunu farkederiz:
zamanOku("Başlangıç zamanı", başlangıçDakikası, başlangıçSaati);
Programcının yaptığı o dikkatsizlik nedeniyle 6:9 olarak girilen bilgi aslında 9:6 olarak algılanmakta, ve daha sonra buna 15:2 süresi eklenmektedir. zamanDizgisi işlevindeki assert de saat değerini 24 olarak görür ve bu yüzden hata atılmasına neden olur.
Burada çözüm, başlangıç zamanının okunduğu noktada parametreleri doğru sırada yazmaktır:
zamanOku("Başlangıç zamanı",
başlangıçSaati,
başlangıçDakikası);
Artık çıktı doğru:
Başlangıç zamanı? (SS:DD) 6:9 İşlem süresi? (SS:DD) 15:2 6:9'da başlayan ve 15 saat 2 dakika süren işlem, 21:11'de sonlanır
assert(ek.length != 0);
O denetimin hatalı çıkması, da ekinin uzunluğunun 0 olduğunu, yani ekin boş olduğunu gösteriyor. Dikkat ederseniz, 6:9 ve 1:1 zamanlarını toplayınca sonuç 7:10 olur. Yani bu sonucun dakika değerinin son hanesi 0'dır. daEki işlevine dikkat ederseniz, 0'ın hangi eki alacağı bildirilmemiş. Çözüm, 0'ın case bloğunu da switch ifadesine eklemektir:
case 6: case 9: case 0: ek = "da"; break;
Bu hatayı da bir assert sayesinde yakalamış ve gidermiş olduk:
Başlangıç zamanı? (SS:DD) 6:9 İşlem süresi? (SS:DD) 1:1 6:9'da başlayan ve 1 saat 1 dakika süren işlem, 7:10'da sonlanır
assert((saat >= 0) && (saat <= 23));
Bunun nedeni, zamanEkle işlevinin saat değerini 23'ten büyük yapabilmesidir. Bu işlevin sonuna, saat değerinin her zaman için 0 ve 23 aralığında olmasını sağlayan bir kalan işlemi ekleyebiliriz:
void zamanEkle( in int başlangıçSaati, in int başlangıçDakikası, in int eklenecekSaat, in int eklenecekDakika, out int sonuçSaati, out int sonuçDakikası) { sonuçSaati = başlangıçSaati + eklenecekSaat; sonuçDakikası = başlangıçDakikası + eklenecekDakika; if (sonuçDakikası > 59) { ++sonuçSaati; } sonuçSaati %= 24; }
Yukarıdaki işlevdeki diğer hatayı da görüyor musunuz? sonuçDakikası 59'dan büyük bir değer olduğunda sonuçSaati bir arttırılıyor, ama sonuçDakikası'nın değeri 59'dan büyük olarak kalıyor.
Belki de şu daha doğru bir işlev olur:
void zamanEkle( in int başlangıçSaati, in int başlangıçDakikası, in int eklenecekSaat, in int eklenecekDakika, out int sonuçSaati, out int sonuçDakikası) { sonuçSaati = başlangıçSaati + eklenecekSaat; sonuçDakikası = başlangıçDakikası + eklenecekDakika; sonuçSaati += sonuçDakikası / 60; sonuçSaati %= 24; assert((sonuçSaati >= 0) && (sonuçSaati <= 23)); assert((sonuçDakikası >= 0) && (sonuçDakikası <= 59)); }
Aslında sonuçDakikası hâlâ bozuk... Ona da 60'tan kalanı atamak gerekir. Ama şimdi işin güzel tarafı, artık bu işlevin hatalı saat ve dakika değerleri üretmesi assert ifadeleri nedeniyle olanaksızdır.
Yukarıdaki işlevi örneğin 6:9 ve 1:55 değerleriyle çağırırsanız, sonuçDakikası'nı denetleyen assert ifadesinin hata vereceğini göreceksiniz.
Ayrıca dikkat ederseniz, sonu 3 ile biten dakika değerlerinde de sorun var:
Başlangıç zamanı? (SS:DD) 1:2
İşlem süresi? (SS:DD) 1:1
1:2'de başlayan ve 1 saat 1 dakika süren işlem, 2:3'da sonlanır
O da unutulmuş bir break anahtar sözcüğünden kaynaklanıyor. Kolay gelsin! :o)