• Sonuç bulunamadı

İdentifier’ın anlamının olduğu program parçasına, kapsam denir. Örneğin bir fonksiyon içinde yerel bir değişken tanımladığımızda bu değşişken sadece o fonksiyon içinde tanımlı ve anlamlıdır. Fonksiyon dışında anlamı yoktur; kullanılmaz. Bu durumda o değişkenin kapsamı o fonksiyondur.

Dört çeşit kapsam vardır:

• fonksiyon kapsamı

• dosya kapsamı

• blok kapsamı

• fonksiyon prototipi kapsamı

Her hangi bir fonksiyonun dışında tanımlanan değişkenin kapsamı tüm programdır ve bu, dosya kapsamı olarak adlandırılır. Böyle bir değişken programın içindeki tüm fonksiyonlarda geçerlidir; kullanılabilir.

Etiketler (“labels” - start: den sonra konan) sadece yazıldığı fonksyonda geçerlidir.

Bunların kapsamı fonksiyondur. Yani fonksiyon dışından bunlara atıfta bulunulamaz.

Etiketler, switch yapıları içinde ve goto cümlelerinde kullanılırlar.

Bir bloğun içinde tanımlanan değişkenlerin kapsamı o bloktur. Blok { ile başlayan ve } ile biten yapıdır. İçiçe blokların olduğu durumda dıştaki ve içteki blokta aynı adlı değişkenler

tanımlandıysa iç blokta dış bloğun aynı adlı değişkeni geçerli olmaz; bu iç blok boyunca dış bloğun değişkeninn değeri “saklanmış” durumdadır.

Fonksiyon prototipinde – anımsanacağı gibi- parametrelerin veri türleri belirtiliyordu. Bu parametrelerin adlarının yazılması gerekmez ancak yazanlar olabilir ancak bu durumda derleyici bu isimleri dikkate almaz, yok sayar. Bu durumda bu isimlerin kapsamı fonksiyon prototipidir.

YAPILAN HATALAR: dış blokta tanımlı bir değişkenin iç blokta da geçerli olmasını istiyorsanız bu değişken iç blokta tanımlanmamalı. Tanımlanırsa bu artık başka bir değişkendir. Bu da programınızda mantık hatasına yol açar.

=Î dış ve iç blokta farklı işlerde kullanılacak değişkenlere aynı adı vermeyin.

ÖRNEK: KAPSAM KURALLARI

// şekil 3.12 a scoping example

#include <iostream>

void a ( void ); // fonksiyon prototipi void b ( void ); // fonksiyon prototipi

void c ( void ); // fonksiyon prototipi int x = 1; // global değişken int main ()

{

int x = 5 ; // main’in yerel değişkeni

cout << “main’in dış bloğundaki x’in değeri = “ << x << endl;

{

int x = 7 ;

cout << “main’in iç bloğundaki x’in değeri = “ << x << endl;

}

cout << “main’in dış bloğundaki x’in değeri = “ << x << endl;

a(); // a fonksiyonunun otomatik yerel x’i var b(); // b fonksiyonunun statik yerel x’i var c(); // c fonksiyonu global x’i kullanıyor

a(); // a, otomatik yerel x’e yeniden ilk değerini atar b(); // b, statik yerel x değeri muhafaz etmektedir c(); // gloabl x de değerini muhafaza etmektedir.

cout << “main’in yerel x değişkeninin değeri = “ << x << endl;

return 0;

}

void a ( void);

{

int x = 25; // a’nın her çağrılışında x’e 25 atanır.

cout << endl << a’ya girişte, a’daki yerel x= “ << x << endl;

++x;

cout << a’dan çıkmadan önce, a’daki yerel x= “ << x << endl;

}

void b ( void);

{

static int x = 50; // b’ye sadece ilk girişte statik // ilk değer atama yapılır.

cout << endl << b’ye girişte, b’deki statik yerel x= “ << x << endl;

++x;

cout << b’den çıkmadan önce, b’daki statik yerel x= “ << x << endl;

}

void c ( void);

{

cout << endl << c’ye girişte, global x = “ << x << endl;

++x;

cout << c’den çıkarken global x= “ << x << endl;

}

PROGRAMIN ÇIKTISI, S. 184’DE

ÖZYİNELEME (“recursion)

Şimdiye dek gördüğümüz programlar genellikle birbirini çağıran fonksiyonlar şeklinde yapılandırılmış programlardı. Bazı problemler için kendisini çağıran fonksiyonlar kullanışlıdır. Özyineli bir fonksiyon (“a recursive function”) doğrudan veya dolaylı olarak kendisini çağıran bir fonksiyondur. Bu konu bilgisayar bilimiyle ilgili daha üst sınıflarda oldukça uzun ve ayrıntılı okunan bir konudur. Bu bölümde, biz, özyinelemenin birkaç örneğini göreceğiz.

Örnek: negatif olmayan bir n sayısının faktöriyeli (n!) = n (n-1)(n-2)(n-3)...1 Özyineli olmayan biçimde (“iterative”)

factoriyel = 1;

for (int sayac = sayi; sayac >=1; sayac--)

faktoriyel *= sayi; olarak kodlayabiliriz.

Faktöriyel fonksiyonunun özyineli (“recursive”) tanımı ise şöyle ifade edilir: n! = n (n –1 )!

Örneğin

5! = 5 . 4 . 3 . 2 . 1 5! = 5 . 4!

5 faktöriyeli bulmak için çağıracağımız fonksiyon bu hesabı yapmnak için 4!’ye gereskinim duyar ve kendisini 4 ile çağırır. 4! için 3!’e gereksinimimiz vardır, aynı fonksiyon 3 ile çağrılır...(şekil. 3.13 s 186 kitapta). Programı yazacak olursak:

// Recursive factorial function s.186

// sıfırdan ona kadar sayıların faktöriyellerini bulan program

#include <iostream>

#include <iomanip>

unsigned long faktoriyel(unsigned long ); // function prototype int main()

// recursive definition of function factorial unsigned long faktoriyel( unsigned long sayi ) {

if ( sayi < = 1 ) // base case – temel durum

return 1;

else

return sayi * faktoriyel(sayi – 1);

}

Programda, unsigned long kullandık. Bilindiği gibi bu, unsigned long int’in kısaltılmış hali.

C++ dilinde, unsigned long int değişkenler 4 bayt yani 32 bitle temsil edilirler. 32 bitle gösterilebilecek sayılar 0 – 4294967295’dir.

Long int değişkenler de 32 bitle gösterilirler. Long int ile temsil edilebilen en küçük ve en büyük sayılar artı/eksi 2147483647’dir. Programımızın çıktısından görüleceği gibi faktöriyel sonucu çok büyük olabilir. N değeri arttıkça faktöriyle çok büyük bir hızla artar. Bunun için unsigned long int kullandık. Bazı durumlarda bu bile yetmeyebilir. Î double daha iyi bir seçim...Ayrıca, C++ daha da büyük sayıları temsil etmemize imkan sağlar.

SIK YAPILAN HATALAR: Özyineleme adımını veya temel durumu (faktöriyel fonksiyonunda 0! ve 1! için 1 döndürülmesi) yanlış yazmak. Bu durumda fonksiyon temek duruma yaklaşamaz ve sonsuz döngüye girerek belleği tüketir.

Diğer bir özyineleme örneği: Fibonnaci Serisi 0, 1, 1, 2, 3, 5, 8, 13, 21, ...

Sıfır ve birden sonraki her sayı kendisinden önce gelen 2 sayının toplamıdır. Birbirini takip eden iki Fibonnacci sayısının oranı 1.618...’e yaklaşır. Bu oran mimarlar, ressamlar, vb arasında "altın oran"”denilen orandır. Bu oran insanlara estetik gelen orandır (pencere ve kapıların en – boy oranları gibi).

SINIFA SOR: Fibonnacci serisini özyineli olarak tanımlayınız:

fibon(0) = 0

fibon(1) = 1

fibon(n) = fibon(n – 1) + fibon(n – 2) Program:

// şekil. 3.15 s. 189

// i. Fibonnaci sayısını özyineli olarak bulan program

#include <iostream>

unsigned long fibon( unsigned long );

int main() {

unsigned long sonuc, sayi;

cout << “bir tamsayı giriniz: “;

cin >> sayi;

sonuc = fibon(sayi);

cout << “Fibonnacci(“ << sayi << “) = “ << sonuc << endl;

return 0;

}

// recursive definition of function fibon unsigned long fibon( unsigned long n) {

if ( n == 0 || n == 1 ) // base case

return n;

else

return fibon(n-1) + fibon (n-2);

}

Bir sayı giriniz: 0 Fibonnacci(0) = 0 Bir sayı giriniz: 1 Fibonnacci(1) = 1 Bir sayi giriniz: 2 Fibonnacci(2) = 1 Bir sayi giriniz: 3 Fibonnacci(3) = 2 Bir sayı giriniz: 4 Fibonnacci(4) = 3 Bir sayı giriniz: 5 Fibonnacci(5) = 5 Bir sayı giriniz: 6 Fibonnacci(6) = 8 Bir sayı giriniz: 10 Fibonnacci (10) = 55 Bir sayı giriniz: 30 Fibonnacci(30) = 832040

f(3) f(2) + f(1)

f(1) + f(0) return 1; şekil.3.16 s.191 return 1 return 0

ÖZYİNELEME (“recursion”) - YİNELEME (“iteration”)

Geçtiğimiz bölümlerde özyineli ve yineli şekilde yazılabilen iki fonksiyon gördük. Iterasyon, bir tekrar yapısı kullanır, özyineleme seçme yapısı kullanır. Özyineleme’de, tekrar, fonksiyonun tekrar tekrar kendisini çağırmasıyla gerçekleştirilir. Iterasyon, döngü devam koşulu yanlış olduğu zaman biter; özyineleme temel duruma gelindiğinde biter.

Özyineleme, yinelemeye göre çok daha fazla bellek alanaı ve işlemci zamanı harcar. Daha fazla bellek alanı harcar çünkü fonksiyonu her çağırışta fonksyonun bir kopyası (fonksiyonun değişkenlerinin bir kopyası) yaratılır. Peki o zaman neden özyinleme kullanırız?

• bazı problemleri özyineli biçimde ifade etmek ve kodlamak anlaşılabilirliği artırır.

Anlamak, takip etmek kolaydır.

ANCAK, performans önemliyse, özyineleme kullanmaktan kaçınmak gerekir.

PARAMETRE LİSTESİ BOŞ OLAN FONKSİYONLAR

Benzer Belgeler