1’den 100’e kadar tamsayıları yazdırmak

Programcılara biraz da eğlence olsun diye sorulan popüler sorulardan biri bu:
1‘den 100‘e kadar tamsayıları bir döngü deyimi, bir goto deyimi ya da özyineleme (recursion) yapısı kullanmadan ekrana yazdırın. Sorunun farklı çözümleri, istenen koşulları sağlayıp sağlamadığı açısından tartışılabilir olsa da C ve C++ dillerinin bazı araçlarının kullanımına örnek olsun diye burada bazı kodları açıklayarak sizinle paylaşacağım. Bu yazı yeni çözümler için ara sıra güncellenecek. İlk çözümümüz şablonları (templates) kullanıyor:

Kodu anlayabildiniz mi? Gelin birlikte bakalım:

struct A bir sınıf şablonu (class template). Şablon tanımında kullanılan N şablon sabit parametresi (template non-type parameter). Yani bu şablondan faydalanarak derleyici, derleme zamanında farklı tamsayı değerleri için farklı sınıfların kodlarını yazacak. Sınıf şablonumuz yine aynı şablonun N – 1 açılımından (specialization) kalıtım yoluyla elde edilmiş.
Yani örneğin A<100> sınıfı A<99> sınıfından kalıtım yoluyla elde edilecek. A sınıfı struct anahtar sözcüğüyle tanımlandığı için varsayılan kalıtım biçiminin public kalıtımı olduğunu hatırlayalım. Sınıf şablonu 0 tamsayı değeri için özelleştirilmiş. A sınıfının 0 açılımı hiçbir koda sahip değil. Bu durumda bu sınıf için derleyici hiçbir şey yapmayan bir varsayılan kurucu işlev (default constructor) yazacak değil mi? Derleyici, A sınıf şablonunun 0 dışında tüm diğer tamsayı açılımları için ana (master) şablonu kullanacak. Bu sınıflarda, sınıfın varsayılan kurucu işlevinin yaptığı tek iş, sabit şablon sabit parametresinin (yani N‘nin) değerini standart çıkış akımına yazdırmak. main işlevi içinde yapılan tanımlamaya bakalım:

A sınıfının 100 açılımı türünden, yani A<100> türünden ismi a olan bir nesne tanımlanıyor. Bu durumda derleyici, kalıtımda taban sınıf olarak kullanılacak A<99> sınıfının da tanımını yani kodunu yazacak. A<99> sınıfı için de yine taban sınıf olarak kullanılacak A<98> sınıfının kodu yazılacak. Derleyici bu durumda A<99> sınıfından A<1> sınıfına kadar tüm sınıfların kodunu benzer şekilde yazmış olacak. İş A<0> sınıfı yazmaya gelince derleyici kurallar gereği bu sabit için özelleştirilmiş şablonu kullanacak, yani bir boş sınıfın tanımını yapacak.
Gelelim çalışma zamanına. Kalıtım yoluyla elde edilmiş bir sınıfın kurucu işlevi (farklı yönde bir düzenleme yapılmamışsa) üye ilk değer verme listesi ile kendi taban sınıfının varsayılan kurucu işlevi ile taban sınıf alt nesnesini hayata getirir, değil mi? Ancak bu nesnenin oluşumundan yani hayata gelmesinden sonra, programın akışı kurucu işlevin ana bloğundaki koda gelir. Bu durumda a nesnesinin hayata gelmesi için onun taban sınıf türünden alt nesnesi olan A<99> nesnesinin hayata gelmesi gerekecek. Bu A<0> alt nesnesinin oluşturulmasına kadar devam edecek. Taban sınıf alt nesneleri hayata geldikten sonra, son oluşturulan taban sınıf nesnesinden başlayarak, yani A<0> nesnesinden başlayarak A<100>‘e kadar oluşturulan tüm taban sınıf nesneleri için çağrılan kurucu işlevlerin ana bloklarındaki kodlar yürütülecek. Kurucu işlev ana bloğu içindeki kod, şablon sabit parametresini değerini standart çıkış akımına verdiğinden, istenen tüm sayılar kullanıcı ekranına yazılmış olacak.

Bu çözümde şüphesiz bir özyineleme var. Ancak bu çalışma zamanına ilişkin değil derleme zamanına ilişkin bir özyineleme.

Şablonlara dayalı bir başka çözüm de bir sınıf şablonunun static üye işlevini kullanmak olabilir:

Derleyici derleme zamanında

ifadesi ile karşılaştığında 100 ayrı sınıfın kodunu yazacak. main içinde yapılan her çağrı bir başka sınıfın static üye işlevinin çağrılması sonucunu doğuracak. N sabitinin 1 değeri için derleyici ana şablon için yapılan özelleştirmeyi kullanacak.

Şimdi de başka bir hileye bakalım:

Bu kez işimizin tamamını A sınıfının varsayılan kurucu işlevi gerçekleştiriyor. A sınıfının kurucu işlevi içinde tanımlanan statik yerel x isimli değişkene 0 ilk değeri veriliyor. Kurucu işlevimiz statik yerel değişkenin değerinin bir fazlasını standart çıkış akımına yazdırıyor. Kullanılan önek ++ işlecinin yan etkisiyle (side effect),  x değişkeninin değeri 1 arttırılıyor. main işlevinde, A sınıfı türünden olan 100 öğeli bir dizinin tanımlandığını görüyorsunuz. Derleyici bu durumda diziyi varsayılan şekilde başlatacak (default initialization) bir kod üretecek, değil mi? Bu da dizinin tüm öğelerinin varsayılan başlatıma tabi tutulacağı anlamına geliyor. Yani dizinin tüm öğeleri için sınıfın varsayılan kurucu işlevi çağrılacak. Böylece [1 100] aralığındaki tüm tamsayılar standart çıkış akımına yazılmış olacak.

Bir de işimizi STL algoritmalarına yaptıralım:

iota algoritmasını kullanarak yerel a dizisini [1 – 100] değerleriyle doldurduk. Daha sonra copy algortiması ile dizinin öğelerini standart çıkış akımına yazdırdık. Bu algoritmaların kodunu derleyici derleme zamanında yazacak. Bu durumda şüphesiz derleyicinin yazacağı işlevlerin kodu döngü deyimlerini içeriyor olacak. En azından döngüler gizlenmiş oldu. 🙂

En iyi çözüm en basit çözümdür diyerek bir de aşağıdaki C koduna bakalım:

Standart olmasa da aşağıdaki kod en ilginç çözümlerden biri olmayı hak ediyor:

Kodda geçen __COUNTER__ makrosuyla ile hiç karşılaşmamış olabilirsiniz. Birçok derleyicinin eklenti (extension) olarak sağladığı bu makro her kullanıldığı yerde bir önceki değerinin bir fazlası olan bir tamsayı sabiti ile değiştiriliyor. Yukarıdai kodun bu durumda nasıl 1‘den 100‘e kadar olan tamsayıları ekrana yazdırdığını anlayabildiniz mi?

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun