Ada Programlama Dili Temelleri

Bölüm 4

Kullanıcı Tanımlı Veri Tipleri

Bölüm 4 Sayfa 3

4.6 Modüler (Çevrimsel) Tamsayı Tipleri

Modüler tamsayı tipleri, Ada programlama diline özgü, başka program dillerinde rastlanmayan sıradışı ve ilginç bir tamsayı türüdür. Bu tamsayı türü kapsam aşımı hatasına neden olmayacak şekilde, kapsam sınırını aşınca otomatik olarak sıfırlanarak taşan sayı kadar değer alarak devam eden, negatif değerler almayan işaretsiz tamsayı (unsigned integer) veri türüdür.

Modüler aritmetik 1801 de Carl Friedrich Gauss tarafından Disquisitiones Arithmeticae (Aritmetik Tartışmalar) adlı kitabında tanıtılmıştır. Günümüze kadar bu konuda kuramsal ve uygulamalı çalışmalar devam etmektedir. Uygulama alanları, matematik, bilgi işlem bilimleri, kriptografi, bankacılık (IBAN numaraları), Chemical Abstract Numaraları, müzik kuramı gibi önemli uygulama alanları olmaktadır. Göreli olarak kuramsal temelleri kolay anlaşılabilen bir konu olmasına karşın, hesaplamaların izlenmesi kolay değildir. Modüler veri tipi Ada programlamasında bu konunun kuramsal ve uygulamalı çalışanlarına yardımcı olmak amacı ile konulmuştur. Bu veri tipi, doğal olarak, konu ile çalışanlara hitap etmektedir ve bu veri tipini kullanacakların modüler aritmetik üzerine bilgi sahibi olmaları yararlı olacaktır.

Modüler tipler, daima 0 dan başlar, negatif değerleri yoktur. Bu yüzden modüler tiplere işaretsiz tamsayı tipi adı da verilir. Modüler tipler, 0 ile modulus adı verilen bir değerden bir eksiğine kadar değerler alabilir. Eğer bu tipten bir nesnenin değeri modulus değerinden fazla olursa, modulus değerinden çıkarılır ve değeri o şekilde belirlenir. Bu şekilde modüler değerler, çevrimsel (siklik) değerlerdir. Bu nedenle modulus değeri çevrim sınırı olarak da adlandırılabilir. Çevrimsel (modüler) tipler, asla kapsam aşımı hatasına neden olmazlar. Bu değerlerin Pred ve Succ nitelikleri de hiçbir zaman kapsam hatasına neden olmazlar.

Modüler tamsayı tipleri aşağıdaki programdaki gibi tanımlanabilir ve kullanılabilirler:


with Ada.Text_IO; use Ada.Text_IO;

procedure b4_6_uyg_1 is

type Çevrimsel is mod 24;

Saat : Çevrimsel := 23;

begin

Saat := Saat + 3;

Put(item =>"Saat  :  " & Çevrimsel'Image(Saat));

end b4_6_uyg_1;
  

Bu programın sonucu :

Saat : 1

olmaktadır.

Programda, çevrimsel değişkenin modulus değeri 24 olarak tanımlanmış olduğundan, ençok 23 değeri ilk değer olarak atanabilir aksi halde başlangıçta kapsam hatası ortaya çıkar. Eğer başlangıç değeri doğru verilirse, bir daha kapsam hatası ortaya çıkmaz. İlk değer 23 olduğundan 23 +1 = 24 olur ve çevrim tamamlanarak Saat değeri sıfırlanır. Geri kalan 2 değeri de sıfır değerine eklendiğinde 0 + 2 = 2 ve Saat değişkeninin değeri 2 olur. Bu durumda, Saat değişkenin değeri çevrimsel olarak 0 -23 değerlerini alabilecektir. Kapsam hatası söz konusu değildir ama görüldüğü gibi çevrimsel bir değişkenin değerinin izlenmesi de kolay değildir.

Çevrimsel değişken değerlerinin, aşağıdaki programda uygulandığı gibi, öntanımlı generik paketlerin kendi özel tipleri için anlık örneklenmesi ile de giriş/çıkış işlemlerinin gerçekleştirilmesi olanağı bulunmaktadır.

with Ada.Text_IO;

procedure b4_6_uyg_2 is

type Çevrimsel is mod 24;

package Çevrimsel_IOis new Ada.Text_IO.Moduler_IO(Çevrimsel);

Saat : Çevrimsel;

begin

Saat := Saat :=-2;

Ada.Text_IO.Put(Item =>"Saat  :  ");

Çevrimsel_IO.Put(item =>Saat);

end b4_6_uyg_2;
  

Bu programın sonucu :

Saat : 22

olmaktadır.

Sonucun 22 olması ilk bakışta şaşırtıcı gelse de, doğru sonuçtur. Saat değeri olarak girilmiş olan değer eksi bir sayı değildir. Çevrimsel değişkenler eksi değerler alamaz. Burada girilen değer tekli bir işlemci olan işaret değişim işlemcisi - ve atanacak çevrimsel değerdir. Burada gerçekleşen olay çevrim sınırı değeri (mod) dan çevrimsel değişkene atanan 2 değerinin çevrim sınırı (mod) değerinden çıkarılmasıdır. Bu şekilde sonuç, 24 - 2 = 22 olarak gerçekleşmektedir. Görüldüğü gibi gerçekten çevrimsel değişkenlerin değerlerinin izlenmesi kolay değildir.

Modüler tamsayı değerleri, işaretli tamsayı değerlerine ve tersine doğru birbirlerine dönüştürülebilir. Bu konuda dikkat edilmesi gereken en önemli konu, dönüştürme sonucunda oluşacak değerin, dönüştürülecek veri tipinin kapsamı içinde olmasıdır. Aksi durumda, kaçınılmaz olarak kapsam hatası oluşur. Bu uyarı aslında tüm veri dönüştürme işlemleri için geçerlidir. Bu konuda aşağıda görülen programda örnek bir uygulama görülmektedir.

with Ada.Text_IO;

with Ada.Integer_Text_IO;

procedure b4_6_uyg_3 is

type Milyem is mod 401;

package Milyem_IO is new Ada.Text_IO.Modular_IO(Milyem);

Açı : Milyem := 402

begin

Ada.Text_IO.Put(Item => "Milyem  Değerine Çevrilecek Tamsayı Değeri :  " );

Ada.Integer_Text_IO.Put(Item => Tamsayı);

Ada.Text_IO.New_Line;

Açı := Milyem(Tamsayı);

Ada.Text_IO.Put(Item => "Tamsayı Değerinin Milyem Karşılığı : ");

Milyem_IO.Put(Item => Açı);

end b4_6_uyg_3;
....

Yukarıdaki programın derlenmesi sonunda, çalıştırma aşamasında sınır aşımı hatası oluşacağı uyarısı verilir. Bu hata, tamsayı değerinin açı değerine dönüştürüleceği a : = Milyem(Tamsayı); adımında ortaya çıkar. Tamsayı değeri 402 dir ve açıya dönüştürülmesi ile, açı değerinin 402 Milyem olması gerekir. Oysa, Milyem değerinin çeverim sınırı 401 dir. Yani Milyem değerleri için 0 .. 400 değerleri tanımlıdır, dönüştürme sonucunda elde edilen 402 değeri, Miilyem çevrimsel (modüler) veri tipinin kapsamı dışındadır ve program çalıştığında kapsam aşımı hatası verecektir.

Tamsayı değerlerinin çevrimsel veri tiplerine dönüştürülmesi sırasında, oluşabilecek kapasam aşımı hatalarının engellenmesi için, Ada programlama dili, kullanıcılara bir olanak sağlamıştır. Bu olanak, tamsayı verilerini hem çevrimsel değerlere dönüştüren, hem de oluşan değerleri çevrimsel veri veri tipinin çevrim sınırları içine yerleştiren Mod niteliğidir. Mod niteliğinin kullanımı aşağıda görülen programda görülmektedir.

with Ada.Text_IO;

with Ada.Integer_Text_IO;

procedure b4_6_uyg_4 is

type Milyem is mod 401;

package Milyem_IO is new Ada.Text_IO.Modular_IO(Milyem);

Açı : Milyem := 402

begin

Ada.Text_IO.Put(Item => "Milyem  Değerine Çevrilecek Tamsayı Değeri :  " );

Ada.Integer_Text_IO.Put(Item => Tamsayı);

Ada.Text_IO.New_Line;

Açı := Milyem'Mod(Tamsayı);

Ada.Text_IO.Put(Item => "Tamsayı Değerinin Milyem Karşılığı : ");

Milyem_IO.Put(Item => Açı);

end b4_6_uyg_4;
..

Bu programın sonucu :

Milyem Değerine Çevrilecek Tamsayı Değeri : 402

Tamsayı Değerinin Milyem Karşılığı : 1

olmaktadır.

Yukarıdaki uygulamadan da görüldüğü gibi, Mod niteliğinin uygulanması, dönüştürme sonunda oluşan değerin, çevrim sınırları içine yerleştirilmesi ile, kapsam hatası oluşmasının önüne geçildiği fakat sonuçta yerleşen değerin öngörülmesinin çok kolay olmayacağı anlaşılmaktadır. Sonuç modüler aritmtik uygulanarak oluşmaktadır ve hesap sistemi, n * modulus - değer şeklindedir. Burada n değeri, sonuç kapsam sınırı içinde kalıncaya kadar arttırılan bir tamsayı, değer ise dönüştürme sonucu oluşan değerdir ve aslında çevrimsel değerler birer tamsayı değeri olduğu için doğrudan doğruya tamsayı verisidir. Yukarıdaki uygulamada, 1 * 401 - 402 = 1 sonucu gerçekleşmiştir.

Çevrimsel veri tiplerinin değerleri birer tamsayı olduklarından, tamsayı veri tiplerine dönüştürülebilirler. Burada yine dikkat edilecek nokta, dönüştürme sonucunda elde edilecek tamsayı tipinin, tipin kapsam sınırları içinde kalmasıdır. Aksi durumda, kaçınılmaz olarak kapsam aşımı hatası oluşacaktır. Aşağıdaki uygulama bir çevrimsel veri tipi değerinin, bir tamsayı veri tipine dönüştürülmesini açıklamaktadır.

with Ada.Text_IO;

with Ada.Integer_Text_IO;

procedure b4_6_uyg_5 is

type Dakika is mod 60;

type İlk_On is range -10 .. 10;

package Dakika_IO is new Ada.Text_IO.Modular_IO(Dakika);

package On_IO is new Ada.Text_IO.Integer_IO(İlk_On);

Süre : Dakika :=8;

Sonuç : İlk_On;

begin

Ada.Text_IO.Put(Item =>"İlk_On  Değerine Çevrilecek Dakika Değeri :  " );

Dakika_IO.Put(item => Süre);

Ada.Text_IO.New_Line;

Sonuç := İlk_On(Süre);

Ada.Text_IO.Put(Item =>"Dakika Değerinin İlk_On Karşılığı : ");

On_IO.Put(Item => Sonuç);

end b4_6_uyg_5;
  

Bu programın sonucu :

İlk_On Değerine Çevrilecek Dakika Değeri : 82

Dakika Değerinin İlk_On Karşılığı : 8

olmaktadır. Dönüştürülen değer, dönüştüğü veri tipinin kapsam alanı içinde kaldığından kapsam aşımı hatası oluşmamaktadır.

Çevrimsel veri tipleri üzerine verebileceğimiz bilgiler bu kadardır. Bu veri tipinin sadece konunun uzmanları tarafından kullanılmasında yarar bulunmaktadır. Bu konularla çalışmayanların genellikle çevrimsel veri tiplerine gereksinmeleri olmayacaktır.

Bu bölümle, basit veri tipi sınıfları, yani daha küçük kısmlara ayrılmayan veri sınıflarının incelenmesi tamamlanmış olmaktadır. Bundan sonra basit veri tiplerinin biraraya getirilmesi ile oluşturulan bileşik veri tiplerini incelemeye başlayacağız. Fakat daha önce basit veri tiplerini kullanarak temel bilgilerimizi geliştirmeye çalışacağız.