Yinelemeli Yapıların Özellikleri ve C’de for Deyimi

Daha önceki yazılarımızda C dilinde yinelemeli yapıları ve bu yapıları implement etmek için kullanılan “while” deyimini gördük. Yineleme dediğimiz olay aslında bir veya daha fazla deyimin sürekli olarak çalıştırılması demektir. Buna döngü adı da verilmektedir. Döngüler çoğunlukla belli koşullar altında işlem yaparlar. Örneğin bir sayaç değişkeni 10 sayısına ulaştığında veya kullanıcı -1 gibi bir sayı girdiğinde döngü sonlanabilir. Daha önceki yazılarda sayaç kontrollü yineleme (counter-controlled iteration) ve nöbetçi değer kontrollü yineleme (sentinel-controlled iteration) kavramlarını da görmüştük.

Sayaç kontollü yinelemede bir sayaç değişkeni yer almaktaydı. Bu değişken döngü içerisinde (genellikle her döngü işleminin sonunda) arttırılarak veya azaltılarak koşulu yanlış hale getirip döngüden çıkmamızı sağlıyordu. Nöbetçi değer kontrollü yinelemede ise program kullanıcıdan bir değer beklemekte ve eğer kullanıcı nöbetçi değer olarak adlandırılan daha önceden belirlenmiş değeri girerse döngüden çıkılmaktaydı.

Biz burada sayaç kontrollü yineleme üzerinde biraz daha duracağız. Bildiğiniz gibi bu yinelemeyi kontol eden şey sayaç değişkeni (counter variable) veya kontol değişkeni (control variable) olarak adlandırılan bir değişkendir. Sayaç kontollü yinelemenin gerçekleştirilebilmesi için şu 4 şeye ihtiyaç vardır:

  1. Sayaç değişkeninin adı.
  2. Sayaç değişkeninin ilk değeri.
  3. Sayaç değişkenin her döngüde arttırılması veya azaltılması.
  4. Sayaç değişkenin son değerini kontol eden bir koşul ifadesi.

Aşağıdaki kod parçasını göz önüne alalım:

int sayac = 1;

while(sayac <= 5) {
    printf("%d\n", sayac);
    sayac++;
}

Bu kod parçasından şunları çıkarabiliriz:

  1. Sayaç değişkeninin adı: sayac
  2. Sayaç değişkeninin ilk değeri: 1
  3. Sayaç değişkeninin her döngüde arttırılıyor veya azaltılıyor mu: Evet (sayac++)
  4. Sayaç değişkeninin sonunu kontol eden bir koşul var mı: Evet (sayac <= 5)

Burada koşul ile sayaç değişkeninin arttırılmasını şu şekilde birleştirebilirdik:

int sayac = 0;

while(++sayac <= 5) {
    printf("%d\n", sayac);
}

Bu kod parçası da az önceki ile aynı işi yapar. Daha önceki yazılarda gördüğümüz “++” operatörü ile bu tip bir kısaltma yapabiliriz. Ancak bu kısaltma bazı programcılar için okunabilirliği azaltabilir.

C programlama dilinde döngüleri implement etmek için sadece while deyimi yoktur. Bu bölümde özellikle sayaç kontrollü yineleme yapılarını implement etmekte kullanılan ve C programlarının olmazsa olmazı for döngü deyiminden bahsedeceğiz. while deyimi parantezler arasında tek bir ifade alır ve bu ifade döngü koşulunu belirtir. Ancak for deyimi ile hem sayaç değişkenini, hem döngü koşulunu, hem de arttırma veya azaltma işlemini tek bir parantez içinde belirtip döngü gövdesinde bu tip ifadeleri kullanmaktan kaçınabiliriz. Böylece daha temiz bir kod yazmış oluruz. for deyiminin genel biçimi şu şekildedir:,

for(<Sayaç Değişkeninin İlk Değeri>; <Döngü Koşulu İfadesi>; <Sayaç Değişkenini Değiştirme İfadesi>) {…}

İlk bakışta karmaşık gelse de aslında bu deyim sayaç kontollü yinelemenin tüm elemanlarını tek seferde yazmamıza izin verir. “;” operatörleri ile parantezin içerisi 3 kısıma ayırılır. İlk kısımda döngü kontrolünde kullanılacak olan sayaç değişkenine ilk değer verilir. Burada istersek sayaç değişkenini direk olarak tanımlayabiliriz veya daha önceden tanımlı olan bir değişkene ilk değer verebiliriz. İkinci kısma ise while döngüsünde olduğu gibi döngü koşulu yazılır. Burada farklı olan bir şey yoktur. Üçüncü kısma sayaç değişkenini değiştiren (arttıran veya azaltan) ifade yazılır. Böylece for döngüsü tamamlanmış olur. Şimdi en başta while ile yazdığımız kod parçasını for ile yazalım:

for(int sayac = 1; sayac <= 5; sayac++) {
    printf("%d\n", sayac);
}

Gördüğünüz gibi oldukça temiz bir yazım elde ettik. İlk kısımda direk sayaç değişkenini tanımlayıp ona ilk değer verdik. İkinci kısımda koşulu belirttik ve son kısımda ise sayaç değişkenini arttıran ifadeyi yazdık. Bu küçük kod parçasından yola çıkarak for deyimin her bir kısmını görsel olarak açıklayalım:

Figür 1: for Deyiminin Açıklaması

Daha önce söylediğim gibi sayaç değişkenini for döngüsünün başlığında veya ondan önce tanımlayabiliriz. Yani şu şekildeki bir kod da geçerli olacaktır:

int sayac;

for(sayac = 1; sayac <= 5; sayac++) {
    printf("%d\n", sayac);
}

Önceki C standartları sadece bu tip bir tanımlamayı kabul ediyordu. Yani for döngüsünü başlığında sadece değişkenlere ilk değer verilebilirdi. Ancak daha sonraki standartlarda tıpkı C++’ta olduğu gibi direk başlıkta değişken tanımlama özelliği de getirilmiştir. Her iki kullanımı da benimseyen programcılar mevcuttur. Bu arada eğer bir değişkeni for döngüsünün başlığında tanımlarsanız, for döngüsü tamamlandığında bu değişken bellekten silinecektir. Aslında bu daha sonra göreceğimiz faaliyet alanı (scope) ile ilgili bir konu olsa da şimdilik değinmekte fayda olduğunu düşünüyorum.

for döngüsünün çalışması diğer deyimlere göre biraz daha farklıdır. Öncelikle sayaç değişkenine ilk değer atayan kısım çalıştırılır ve hemen ardından döngü koşulu kontrol edilir. Eğer koşul sağlanıyorsa döngü gövdesindeki deyimler çalıştırılır. Hemen ardından ise for döngüsünün başlığında yer alan ve sayacın değerini değiştiren ifade çağrılır. Sonra yine koşul kontol edilir ve bu böyle devam eder. En son koşul yanlış olduğunda döngüden çıkılır. Az önceki kod parçası üzerinden bu çalışmayı anlatan şöyle bir GIF resmi hazırladım:

Figür 2: for Döngüsünün Çalışma Prensibi

Burada gördüğünüz gibi sayaç değişkenine ilk değer atama kısmı sadece 1 kez çalıştırılır. Daha sonra diğer kısımlar arasında bir döngü kurulur ve en son koşul kontrol edildiğinde, eğer koşul yanlış çıkarsa döngüden çıkılır. Burada da yine döngü gövdesinde tek satır bulunuyorsa süslü parantezleri kullanmaya gerek olmayabilir:

for(int sayac = 1; sayac <= 5; sayac++)
    printf("%d\n", sayac);

Fakat bu çoğu zaman okunabilirliği azaltan bir durum olduğu için tercih edilmez. Yine döngü koşulunda “<=” yerine “<” kullanmak Off-By-One Error olarak adlandırılan bir hataya neden olur. Bu durumda döngü istediğiniz sayının 1 eksiği kadar döner. Bu bir mantıksal hatadır ve bazen çözülmesi zor olabilir. Bu durumda kullandığınız ilişkisel operatörlere dikkat etmenizde fayda vardır.

sayac = sayac + 1
sayac += 1
sayac++
++sayac

++” ve “” operatörlerini incelerken tek değişken içeren bir deyimde bu operatörleri değişkenin neresinde kullanırsak kullanalım aynı anlama geldiğini söylemiştik. İşte burada tek değişken içeren deyim gibi bir durum söz konusu olduğundan, bu operatörleri sayaç değişkeninin başında veya sonunda kullanmamızın döngü içeriği açısından bir önemi yoktur. Ancak sayaç değişkeni ile döngüden sonra bir işimiz olursa o zaman durum değişebilir. Ancak bu nadir rastlanan bir durumdur.

Sayaç değişkenini tanımladığımız (veya ilk değer verdiğimiz) kısımda ve sayaç değişkenini arttırdığımız veya azalttığımız kısımda virgül operatörü ile birden fazla ifade kullanak mümkündür. Böylece bir döngüde birden fazla sayaç değişkenini kullanabilir hale geliriz. Virgül operatörünün (,) öncelik yönü (associativity) bildiğiniz gibi soldan sağa olduğundan, ilk olarak en soldaki ifade, son olarak da en sağdaki ifade çalıştırılacaktır. Şu kod parçasına bakalım:

for(int i = 0, j = 0; i <= 8 && j <= 4; i++, j++) {
    printf("%d, %d\n", i, j);
}

Bu kod parçasını çalıştırdığımızda şöyle bir çıktı elde ederiz:

ÇIKTI
0, 0
1, 1
2, 2
3, 3
4, 4

Burada öncelikle i = 0, daha sonra da j = 0 ifadesi çalıştırılarak koşul ifadesi kontol edilecek ve daha sonra da döngü gövdesindeki ifadeler çalıştırılacaktır. Döngü gövdesinden çıkıldığından ise önce i++ ve daha sonra da j++ ifadesi çalıştırılacak ve yine döngü koşulu kontrol edilecektir. Döngü koşulu yanlış olana kadara tıpkı az önce gördüğümüz gibi bir yineleme oluşacaktır. Döngü koşulunda gördüğünüz gibi j = 5 olduğunda döngü koşulu yanlış olacak ve döngüden çıkılacaktır.

for döngüsünün başlığında bildiğiniz gibi 3 adet ifade yazılmaktadır. Ancak bu ifadelerin yazılma zorunluluğu yoktur. Yani başlangıç değeri atama kısmı, koşul kısmı veya sayaç değişkenini değiştirme kısmı boş bırakılabilir. Ancak “;” işaretlerinin yine de konulması gereklidir. Örneğin sayaç değişkenine başlangıç değeri atama işlemini döngü deyiminin dışında yapıp o kısmı boş bırakalım:

int sayac = 0;

for(; sayac <= 5; sayac++) {
    printf("%d\n", sayac);
}

Şimdi de sayaç değişkenini arttırma işini döngü gövdesinin sonuna yerleştirelim ve o kısmı da boş bırakalım:

int sayac = 0;

for(; sayac <= 5;) {
    printf("%d\n", sayac);
    sayac++;
}

Gördüğünüz gibi for deyimin bu haliyle “;” işaretleri dışında while deyiminden hiçbir farkı kalmamıştır. Bunun yanında koşul kısmını da boş bırakabiliriz. Bu durumda C derleyicisi döngü koşulunu sürekli doğru kabul edecek ve bir sonsuz döngü meydana gelecektir:

int sayac = 0;

for(;;) {
    printf("%d\n", sayac);
    sayac++;
}

Her yeri boş bıraksak bile “;” işaretlerini koymayı unutmamalıyız. Buraya kadar for döngüsü ile ilgili temel tanımlamaları yaptığımızı düşünüyorum. Son olarak bir örnek vererek bu yazıyı sonlandıralım. Burada faktöriyel hesabı yapan, yani kullanıcının girdiği bir “n” sayısını ona “n!” işleminin bir sonucu olarak döndüren bir program yazmak istiyoruz. Bu for döngüsü kullanılarak oldukça basit bir şekilde yazılabilir:

// Code 4.1.1: Code4_1_1.c
// Arttırma ve azaltma operatörleri örneği

#include <stdio.h>

int main()
{
	int sonuc = 1;

	int n;
	printf("Faktoriyeli alinacak sayiyi girin: ");
	scanf("%d", &n);

	for(int i = 1; i <= n; i++)
	{
		sonuc *= i;
	}
	
	printf("%d! = %d\n", n, sonuc);	

	return 0;
}
ÇIKTI
Faktoriyeli alinacak sayiyi girin: 6
6! = 720

Burada sonucu tutacak olan sonuc değişkeni ile sayaç değişkeni belirtecek olan i değişkenlerinin ilk değerini 1 olarak belirlediğimize dikkat edin. Faktöriyel hesabında sürekli çarpma işlemi olduğundan ve 0 çarpmada yutan eleman olduğundan, bu değişkenlerin ilk değerlerini 1 olarak belirliyoruz. 16. satırdaki çarpma işleminde her döngüde sayaç değişkeninin o anki değeri ile sonuc değişkeninin o anki değerini sürekli çarpıyoruz. Döngü sonunda ise girdiğimiz sayının faktöriyelini elde etmiş oluyoruz.

Bu bölümde yinelemeli yapılar ile ilgili çeşitli tekrarlar yapıp bu yapılardan özellikle sayaç kontrollü yinelemeyi kolayca implement etmek için kullanılan for döngüsünü gördük. Böylelikle C dilinde döngüleri implement etmek için elimizde 2 adet deyim olmuş oldu. Bu kısımda döngüler ile ilgili son bir deyim göreceğiz. Ama for döngüsü içlerinden en önemlisi olduğu için bir sonraki yazıda onunla ilgili çeşitli özelliklere değinecek ve daha fazla örnek yapacağız.

5 2 votes
Article Rating
Subscribe
Bildir
guest

0 Yorum
Eskiler
En Yeniler Beğenilenler