Yapılar
Derslerin başında temel türlerin üst düzey kavramları ifade etmede yetersiz kalacaklarını söylemiştim. int türünden bir tamsayı örneğin iki olay arasında geçen süreyi dakika türünden ifade etmek için kullanılabilir; ama böyle bir değer her zaman tek başına kullanışlı olamaz. Değişkenler bazen başka değişkenlerle bir araya geldiklerinde anlam kazanırlar.
Yapılar, temel türleri veya başka yapıları bir araya getirerek yeni türler oluşturmaya yarayan olanaklardır. Yeni tür, struct anahtar sözcüğü ile oluşturulur. struct, "yapı" anlamına gelen "structure"ın kısaltmasıdır.
Bu derste yapılarla ilgili olarak anlatacağım çoğu bilgi, daha sonra göreceğimiz sınıfları anlamada da yardımcı olacak. Özellikle bir araya getirerek yeni tür tanımlama kavramı, yapılarda ve sınıflarda aynıdır.
Yapı kavramını anlamak için daha önce assert dersinde gördüğümüz zamanEkle işlevine bakalım:
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çDakikası = başlangıçDakikası + eklenecekDakika; sonuçSaati = başlangıçSaati + eklenecekSaat; sonuçSaati += sonuçDakikası / 60; sonuçDakikası %= 60; sonuçSaati %= 24; }
Not: İşlevin in, out, ve unittest bloklarını fazla yer tutmamak için bu derste göstermiyorum.
Her ne kadar o işlev altı tane parametre alıyor gibi görünse de, birbirleriyle ilgili olan parametreleri çifter çifter düşünürsek, aslında üç çift bilgi aldığını görürüz. O çiftlerden ilk ikisi giriş olarak, sonuncusu da çıkış olarak kullanılmaktadır.
Tanımlanması
İşte struct, birbirleriyle ilişkili değişkenleri bir araya getirerek yeni bir tür olarak kullanma olanağı verir:
struct GününSaati { int saat; int dakika; }
Yukarıdaki tanım, saat ve dakika isimli iki int değişkeni bir araya getiren ve ismi GününSaati olan yeni bir tür tanımlar. Yukarıdaki tanımdan sonra artık başka türler gibi kullanabileceğimiz GününSaati isminde yeni bir türümüz olur. Örnek olarak int türüne benzer kullanımını şöyle gösterebiliriz:
int sayı; // bir değişken sayı = başkaSayı; // başkaSayı'nın değerini alması GününSaati zaman; // bir nesne zaman = başkaZaman; // başkaZaman'ın değerini alması
Yapı türleri şöyle tanımlanır:
struct Türİsmi { // ... türü oluşturan üyeler ve varsa özel işlevleri ... }
Yapılar için özel işlevler de tanımlanabilir. Bunları daha sonraki bir derste anlatacağım. Bu derste yalnızca yapı üyelerini gösteriyorum.
Yapının bir araya getirdiği parçalara üye adı verilir. Bu tanıma göre, yukarıdaki GününSaati yapısının iki üyesi vardır: saat ve dakika.
Yapı tanımı, tür tanımıdır; nesne tanımı değildir
Burada bir uyarıda bulunmam gerekiyor: İsim Alanı dersinde ve Yaşam Süreçleri dersinde anlatılanlar doğrultusunda; yapı tanımında kullanılan küme parantezlerine bakarak, o kapsam içindeki üyelerin yapının tanımlandığı an yaşamaya başladıklarını düşünebilirsiniz. Bu doğru değildir.
Yapı tanımı, değişken tanımlamaz:
struct GününSaati { int saat; // ← değişken tanımı değil! int dakika; // ← değişken tanımı değil! }
Yapı tanımı, daha sonradan yapı nesneleri oluşturulduğunda ne tür üye değişkenlerinin olacağını belirler:
struct GününSaati { int saat; // ← sonra, nesnenin içinde oluşturulacak int dakika; // ← sonra, nesnenin içinde oluşturulacak }
O üye değişkenler; bu yapı türünden bir nesne oluşturulduğu zaman, o nesnenin parçası olarak oluşturulurlar:
GününSaati yatmaZamanı; // içinde kendi saat ve dakika // değişkenlerini barındırır GününSaati kalkmaZamanı; // bu da kendi saat ve dakika // değişkenlerini barındırır; // bunun saat ve dakika // değişkenleri öncekinden // bağımsızdır
Kodlama kolaylığı
Saat ve dakika gibi iki bilgiyi böyle bir araya getirerek tek bir tür gibi kullanabilmek büyük kolaylık sağlar. Örneğin yukarıdaki işlev altı tane int yerine, asıl amacına çok daha uygun olacak şekilde üç tane GününSaati türünde parametre alabilir:
void zamanEkle(in GününSaati başlangıç, in GününSaati eklenecek, out GününSaati sonuç) { // ... }
Not: Günün saatini belirten böyle iki değerin eklenmesi aslında normal bir işlem olarak kabul edilmemelidir. Örneğin kahvaltı zamanı olan 7:30'a öğle yemeği zamanı olan 12:00'yi eklemek doğal değildir. Burada aslında Süre diye yeni bir tür tanımlamak ve GününSaati nesnelerine Süre nesnelerini eklemek çok daha doğru olurdu. Ben bu derste yine de yalnızca GününSaati türünü kullanacağım.
Hatırlayacağınız gibi, işlevler return deyimiyle tek bir değer döndürebilirler. zamanEkle ürettiği saat ve dakika değerlerini zaten bu yüzden iki tane out parametre olarak tanımlamak zorunda kalıyordu. Ürettiği iki tane sonucu tek bir değer olarak döndüremiyordu.
Yapılar bu kısıtlamayı da ortadan kaldırırlar: Birden fazla bilgiyi bir araya getirerek tek bir tür oluşturdukları için, işlevlerin dönüş türü olarak kullanılabilirler. Artık işlevden tek bir GününSaati nesnesi döndürebiliriz:
GününSaati zamanEkle(in GününSaati başlangıç, in GününSaati eklenecek) { // ... }
Böylece zamanEkle artık yan etki oluşturan değil, değer üreten bir işlev haline de gelmiş olur. İşlevler dersinden hatırlayacağınız gibi; işlevlerin yan etki oluşturmak yerine değer üretmeleri tercih edilir.
Yapılar da yapı üyesi olabilirler. Örneğin GününSaati yapısından iki üyesi bulunan başka bir yapı şöyle tasarlanabilir:
struct Toplantı { string konu; int katılımcıSayısı; GününSaati başlangıç; GününSaati bitiş; }
Toplantı yapısı da başka bir yapının üyesi olabilir. Yemek diye bir yapı olduğunu da varsayarsak:
struct GünlükPlan
{
Toplantı projeToplantısı;
Yemek öğleYemeği;
Toplantı bütçeToplantısı;
}
Üye erişimi
Yapı üyelerini de herhangi bir değişken gibi kullanabiliriz. Tek fark, üyelere erişmek için nesnenin isminden sonra önce erişim işleci olan nokta, ondan sonra da üyenin isminin yazılmasıdır:
başlangıç.saat = 10;
O satır, başlangıç nesnesinin saat üyesine 10 değerini atar.
Yapılarla ilgili bu kadarlık bilgiyi kullanarak zamanEkle işlevini artık şu şekilde değiştirebiliriz:
GününSaati zamanEkle(in GününSaati başlangıç, in GününSaati eklenecek) { GününSaati sonuç; sonuç.dakika = başlangıç.dakika + eklenecek.dakika; sonuç.saat = başlangıç.saat + eklenecek.saat; sonuç.saat += sonuç.dakika / 60; sonuç.dakika %= 60; sonuç.saat %= 24; return sonuç; }
Bu işlevde kullanılan değişken isimlerinin artık çok daha kısa seçilebildiğine dikkat edin: nesnelere başlangıç, eklenecek, ve sonuç gibi kısa isimler verebiliyoruz. başlangıçSaati gibi bileşik isimler kullanmak yerine de nesnelerin üyelerine nokta ile başlangıç.saat şeklinde erişiyoruz.
O işlevi kullanan bir kod aşağıdaki şekilde yazılabilir. Bu program, 1 saat 15 dakika süren ve 8:30'da başlayan dersin bitiş zamanını 9:45 olarak hesaplar:
void main() { GününSaati dersBaşı; dersBaşı.saat = 8; dersBaşı.dakika = 30; GününSaati dersSüresi; dersSüresi.saat = 1; dersSüresi.dakika = 15; const GününSaati dersSonu = zamanEkle(dersBaşı, dersSüresi); dout.writefln( "Ders sonu: %s:%s", dersSonu.saat, dersSonu.dakika); }
Ders sonu: 9:45
Yukarıdaki main'i şimdiye kadar bildiklerimizi kullanarak yazdım. Biraz aşağıda bu işlemlerin çok daha kolay ve kısa olanlarını göstereceğim.
Kurma
Yukarıdaki main'in ilk üç satırı, dersBaşı nesnesinin kurulması ile ilgilidir; ondan sonraki üç satır da dersSüresi nesnesinin kurulmasıdır. O satırlarda önce nesne tanımlanmakta, sonra saat ve dakika üyelerinin değerleri atanmaktadır.
Herhangi bir değişkenin veya nesnenin tutarlı bir şekilde kullanılabilmesi için mutlaka kurulması gerekir. Bu çok önemli ve yaygın bir işlem olduğu için, yapı nesneleri için kısa bir kurma söz dizimi vardır:
GününSaati dersBaşı = GününSaati(8, 30);
GününSaati dersSüresi = GününSaati(1, 15);
Bu yazım, türü otomatik olarak sağ taraftan çıkarsayan auto anahtar sözcüğü ile daha da kısa hale getirilebilir:
auto dersBaşı = GününSaati(8, 30); auto dersSüresi = GününSaati(1, 15);
Nesneler bu şekilde kurulurken belirtilen değerler, yapının üyelerine yapı içinde tanımlandıkları sıra ile atanırlar: yapı içinde saat önce tanımlandığı için 8 değeri dersBaşı.saat'e, 30 değeri de dersBaşı.dakika'ya atanır.
const olarak kurabilme olanağı
Nesneleri aynı anda hem tanımlamak hem de değerlerini verebilmek, onları const olarak işaretleme olanağı da sağlar:
const auto dersBaşı = GününSaati(8, 30); const auto dersSüresi = GününSaati(1, 15);
Kurulduktan sonra artık hiç değişmeyecek oldukları durumlarda, bu nesnelerin sonraki satırlarda yanlışlıkla değiştirilmeleri böylece önlenmiş olur. Yukarıdaki programda ise nesneleri const olarak işaretleyemezdik, çünkü ondan sonra üyelerinin değerlerini atamamız mümkün olmazdı:
const GününSaati dersBaşı; dersBaşı.saat = 8; // ← derleme HATASI dersBaşı.dakika = 30; // ← derleme HATASI
const olarak işaretlenmiş olan sabit dersBaşı nesnesinin üyelerini değiştirmek yasal değildir.
Sondaki üyelerin değerleri boş bırakılabilir
Yapı nesneleri kurulurken sondaki üyelerin değerleri belirtilmeyebilir. Bu durumda sondaki üyeler yine de otomatik olarak kendi türlerinin .init değeri ile ilklenirler.
Bunu gösteren aşağıdaki programda Deneme türü gittikçe azalan sayıda parametre ile kuruluyor ve geri kalan parametrelerin de otomatik olarak ilklendikleri assert ifadeleri ile gösteriliyor (programda kullanmak zorunda kaldığım isnan işlevini programdan sonra açıklıyorum):
import std.math; struct Deneme { char karakter; int tamsayı; double kesirli; } void main() { // Bütün değerlerle auto d1 = Deneme('a', 1, 2.3); assert(d1.karakter == 'a'); assert(d1.tamsayı == 1); assert(d1.kesirli == 2.3); // Sonuncusu eksik auto d2 = Deneme('a', 1); assert(d2.karakter == 'a'); assert(d2.tamsayı == 1); assert(isnan(d2.kesirli)); // Son ikisi eksik auto d3 = Deneme('a'); assert(d3.karakter == 'a'); assert(d3.tamsayı == int.init); assert(isnan(d3.kesirli)); // Hiçbirisi yok auto d4 = Deneme(); assert(d4.karakter == char.init); assert(d4.tamsayı == int.init); assert(isnan(d4.kesirli)); // Yukarıdakiyle aynı şey Deneme d5; assert(d5.karakter == char.init); assert(d5.tamsayı == int.init); assert(isnan(d5.kesirli)); }
Kesirli sayılar dersinden hatırlayacağınız gibi, double'ın ilk değerini bildiren double.init niteliğinin değeri double.nan'dır. nan da kesirli sayılarda "geçersiz değer" anlamına geldiği için eşitlik karşılaştırmalarında bile kullanılamaz. O yüzden programda d2.kesirli == double.init yerine isnan(d2.kesirli) yazmak zorunda kaldım. std.math modülünde tanımlanan isnan işlevi, kendisine verilen değerin nan'a eşit olup olmadığını bildirir.
Varsayılan üye değerlerinin belirlenmesi
Üyelerin otomatik olarak ilkleniyor olmaları çok yararlı bir olanaktır. Üyelerin rastgele değerlerle kullanılmaları önlenmiş olur. Ancak, her üyenin kendi türünün .init değerini alması her duruma uygun değildir. Örneğin char.init değeri geçerli bir karakter bile değildir.
Bu yüzden üyelerin otomatik olarak alacakları değerler programcı tarafından belirlenebilir. Bu sayede örneğin yukarıda gördüğümüz ve hiçbir kullanışlılığı olmayan double.nan değeri yerine, çoğu zaman çok daha uygun olan 0.0 değerini kullanabiliriz.
Üyelerin aldıkları bu özel ilk değerlere varsayılan değer denir ve üye tanımından sonraki atama söz dizimiyle belirlenir:
struct Deneme { char karakter = 'A'; int tamsayı = 11; double kesirli = 0.25; }
Tür tanımı sırasında kullanılan bu yazım şeklinin bir atama işlemi olmadığına dikkat edin. Yukarıdaki kodun tek amacı, üyeler için hangi değerlerin varsayılacağını belirlemektir. Bu değerler, daha sonra nesne oluşturulurken gerekirse kullanılacaktır.
Nesne kurulurken değerleri özellikle belirtilmeyen üyeler o varsayılan değerleri alırlar. Örneğin aşağıdaki kullanımda nesnenin hiçbir üyesinin değeri verilmemektedir:
Deneme d; // hiçbir üye değeri belirtilmiyor dout.writefln("%s,%s,%s", d.karakter, d.tamsayı, d.kesirli);
Buna rağmen bütün üyeler türün tanımında belirtilmiş olan ilk değerlere sahip olurlar:
A,11,0.25
{ } karakterleriyle kurma
Yukarıdaki kurma söz dizimi varken kullanmaya gerek olmasa da, bunu da bilmeniz gerekir. Yapı nesnelerini başka bir söz dizimiyle de kurabilirsiniz:
GününSaati dersBaşı = { 8, 30 };
Belirlenen değerler bu kullanımda da üyelere sıra ile atanırlar; ve bu kullanımda da üye sayısından daha az değer verilebilir.
Not: dmd'nin bir hatası nedeniyle, bu dersi yazdığım sıralarda değerleri belirtilmeyen üyelerin ilk değerleri atanmıyor. İlk değerleri atanmadığı için de üyelerin değerleri belirsizdir ve dikkat edilmezse programda hataya neden olabilir.
D'ye C++ yoluyla C'den geçen bu söz dizimi yerine, nesneleri yukarıda gösterdiğim şekilde kurmanızı öneririm:
auto dersBaşı = GününSaati(8, 30); // ← Önerilen GününSaati dersSonu = { 9, 30 }; // ← C'deki gibi
Kopyalama ve Atama
Yapılar değer türleridir. Bundan; Değerler ve Referanslar dersinde açıklandığı gibi, her yapı nesnesinin kendisine ait değeri olduğunu anlarız. Kurulduklarında kendi değerlerini edinirler; atandıklarında da yalnızca kendi değerleri değişir. (Not: Bu konuda referans türünden olan üyelere özellikle dikkat etmek gerekir. Bunu biraz aşağıda açıklıyorum.)
auto seninYemekSaatin = GününSaati(12, 0); auto benimYemekSaatim = seninYemekSaatin; // Yalnızca benim yemek saatim 12:05 olur: benimYemekSaatim.dakika += 5; // ... senin yemek saatin değişmez: assert(seninYemekSaatin.dakika == 0);
Kopyalama sırasında bir nesnenin bütün üyeleri sırayla diğer üyeye kopyalanır.
Benzer şekilde, atama işlemi de bütün üyelerin sırayla atanmaları anlamına gelir.
Referans türünden olan üyelere dikkat!
Burada çok önemli bir konuyu hatırlatmak gerekiyor: referans türünden olan değişkenler kopyalandıklarında veya atandıklarında asıl nesne değişmez, ona erişim sağlayan referans değişir, ve sonuçta asıl nesneye birden fazla referans tarafından erişim sağlanmış olur.
Bunun yapı üyeleri açısından önemi, iki farklı yapı nesnesinin üyelerinin aynı asıl nesneye erişim sağlıyor olacaklarıdır. Bunu görmek için referans türünden bir üyesi olan bir yapıya bakalım. Bir öğrencinin numarasını ve notlarını içeren şöyle bir yapı tanımlanmış olsun:
struct Öğrenci { int numara; int[] notlar; }
O türden bir nesnenin başka bir nesnenin değeriyle kurulduğu şu koda bakalım:
// Birinci öğrenciyi kuralım: auto öğrenci1 = Öğrenci(1, [ 70, 90, 85 ]); // İkinci öğrenciyi birincinin kopyası olarak kuralım ... auto öğrenci2 = öğrenci1; // ... ve sonra numarasını değiştirelim: öğrenci2.numara = 2; // İlk öğrencinin notunda bir değişiklik yaptığımızda ... öğrenci1.notlar[0] += 5; // ... ikinci öğrencinin notunun da değiştiğini görürüz: dout.writefln(öğrenci2.notlar[0]);
öğrenci2 nesnesi kurulduğu zaman, üyeleri sırayla öğrenci1'in üyelerinin değerlerini alır. int bir değer türü olduğu için, her iki Öğrenci nesnesinin ayrı numara değeri olur.
Her iki Öğrenci nesnesinin birbirlerinden bağımsız olan notlar üyeleri de vardır. Ancak, dizi dilimleri referans türleri olduklarından, her ne kadar notlar üyeleri bağımsız olsalar da, aslında aynı asıl dizinin elemanlarına erişim sağlarlar. Sonuçta, bir nesnenin notlar üyesinde yapılan değişiklik diğerini de etkiler.
Yukarıdaki kodun çıktısı, iki öğrenci nesnesinin aynı asıl notları paylaştıklarını gösterir:
75
Belki de burada hiç kopyalama işlemini kullanmadan, ikinci nesneyi kendi numarasıyla ve birincinin notlarının kopyasıyla kurmak daha doğru olur:
// İkinci öğrenciyi birincinin notlarının kopyası ile // kuruyoruz auto öğrenci2 = Öğrenci(2, öğrenci1.notlar.dup); // İlk öğrencinin notunda bir değişiklik yaptığımızda ... öğrenci1.notlar[0] += 5; // ... ikinci öğrencinin notu bu sefer değişmez: dout.writefln(öğrenci2.notlar[0]);
Dizilerin .dup niteliği ile kopyalandığı için bu sefer her nesnenin ayrı notları olur. Şimdiki çıktı, ikinci öğrencinin notunun etkilenmediğini gösterir:
70
Not: İstenen durumlarda referans türünden üyelerin otomatik olarak kopyalanmaları da sağlanabilir. Bunu daha sonra yapı işlevlerini anlatırken göstereceğim.
Yapı hazır değerleri
Nasıl 10 gibi hazır değerleri hiç değişken tanımlamak zorunda kalmadan işlemlerde kullanabiliyorsak, yapı nesnelerini de isimleri olmayan hazır değerler olarak kullanabiliriz.
Yapı hazır değerlerini oluşturmak için yine kurma söz dizimi kullanılır ve yapı nesnesi gereken her yerde kullanılabilir.
GününSaati(8, 30) // ← hazır değer
Yukarıdaki main işlevini şimdiye kadar öğrendiklerimizi kullanarak şöyle yazabiliriz:
void main() { const auto dersBaşı = GününSaati(8, 30); const auto dersSüresi = GününSaati(1, 15); const GününSaati dersSonu = zamanEkle(dersBaşı, dersSüresi); dout.writefln( "Ders sonu: %s:%s", dersSonu.saat, dersSonu.dakika); }
Dikkat ederseniz, o programda dersBaşı ve dersSüresi nesnelerinin açıkça belirtilmelerine gerek yoktur. Onlar yalnızca dersSonu nesnesini hesaplamak için kullanılan aslında geçici nesnelerdir. O nesneleri açıkça tanımlamak yerine, zamanEkle işlevine şu şekilde hazır değer olarak da gönderebiliriz:
void main() { const GününSaati dersSonu = zamanEkle(GününSaati(8, 30), GününSaati(1, 15)); dout.writefln( "Ders sonu: %s:%s", dersSonu.saat, dersSonu.dakika); }
static üyeler
Çoğu durumda her yapı nesnesinin kendi üyelerine sahip olmasını isteriz. Bazı durumlarda ise belirli bir yapı türünden olan bütün nesnelerin tek bir değişkeni paylaşmaları uygun olabilir. Bu, o yapı türü için akılda tutulması gereken genel bir bilgi bulunduğunda gerekebilir.
Bütün nesnelerin tek bir üyeyi paylaşmalarının bir örneği olarak, her bir nesne için farklı bir tanıtıcı numara olduğu bir durum düşünelim:
struct Nokta { // Her nesnenin kendi tanıtıcı numarası int numara; int satır; int sütun; }
Her nesneye farklı bir numara verebilmek için sonrakiNumara gibi bir değişken barındırmak, ve her nesne için o sayıyı bir arttırmak gerekir:
Nokta NoktaOluştur(int satır, int sütun) { int numara = sonrakiNumara; ++sonrakiNumara; return Nokta(numara, satır, sütun); }
Burada karar verilmesi gereken şey, her nesnenin oluşturulması sırasında ortak olarak kullanılan sonrakiNumara bilgisinin nerede tanımlanacağıdır. static üyeler işte bu gibi durumlarda kullanışlıdırlar.
O bilgi bir yapı üyesi olarak tanımlanır ve static olarak işaretlenir. Diğer üyelerin aksine, böyle üyelerden yalnızca bir adet oluşturulur:
import std.cstream; struct Nokta { // Her nesnenin kendi tanıtıcı numarası int numara; int satır; int sütun; // Bundan sonra oluşturulacak olan nesnenin numarası static int sonrakiNumara; } Nokta NoktaOluştur(int satır, int sütun) { int numara = Nokta.sonrakiNumara; ++Nokta.sonrakiNumara; return Nokta(numara, satır, sütun); } void main() { auto üstteki = NoktaOluştur(7, 0); auto ortadaki = NoktaOluştur(8, 0); auto alttaki = NoktaOluştur(9, 0); dout.writefln(üstteki.numara); dout.writefln(ortadaki.numara); dout.writefln(alttaki.numara); }
sonrakiNumara her seferinde bir arttırıldığı için her nesnenin farklı numarası olur:
0 1 2
static üyeler bütün türe ait olduklarından, onlara erişmek için bir nesne bulunması gerekmez. Yukarıda olduğu gibi, o üyeye türün ismi kullanılarak da erişilebilir:
++alttaki.sonrakiNumara;
++Nokta.sonrakiNumara; // üst satırın eşdeğeri
Problemler
- Tek bir oyun kağıdını temsil eden ve ismi
OyunKağıdıolan bir yapı tasarlayın. Bu yapının kağıt rengi ve kağıt değeri için iki üyesi olduğu düşünülebilir. - Bir
OyunKağıdınesnesi alan ve o nesneyi çıkışa yazdıranoyunKağıdıYazdırisminde bir işlev tanımlayın: - İsmi
yeniDesteolan bir işlev yazın. Elli iki farklı oyun kağıdını temsil edenOyunKağıdı[] türünde bir dizi döndürsün: - Desteyi karıştıran bir işlev yazın.
std.randommodülünde tanımlı olanuniformişlevini kullanarak rastgele seçtiği iki kağıdın yerini değiştirsin. Bu işlemi kaç kere tekrarlayacağını da parametre olarak alsın:
Renk için bir enum değer kullanabileceğiniz gibi; doğrudan ♠, ♡, ♢, ve ♣ karakterlerini de kullanabilirsiniz.
Kağıt değeri olarak da bir int veya bir dchar üye kullanabilirsiniz. int seçerseniz 1'den 13'e kadar değerlerden belki de 1, 11, 12, ve 13 değerlerini sırasıyla as, vale, kız ve papaz için düşünebilirsiniz.
Daha başka çözümler de bulunabilir. Örneğin kağıt değerini de bir enum olarak tanımlayabilirsiniz.
Bu yapının nesnelerinin nasıl kurulacakları, üyeler için seçtiğiniz türlere bağlı olacak. Örneğin eğer her iki üyeyi de dchar türünde tasarladıysanız, şöyle kurulabilirler:
auto kağıt = OyunKağıdı('♣', '2');
void oyunKağıdıYazdır(in OyunKağıdı kağıt) { // ... burasını siz yazın ... } void main() { auto kağıt = OyunKağıdı(/* ... */); oyunKağıdıYazdır(kağıt); }
Örneğin sinek ikiliyi çıktıya şu şekilde yazdırsın:
♣2
Kupa asını da şu şekilde:
♡A
O işlevin içeriği, doğal olarak yapıyı nasıl tasarladığınıza bağlı olacaktır.
OyunKağıdı[] yeniDeste() out (sonuç) { assert(sonuç.length == 52); } body { // ... burasını siz yazın ... }
Bu işlev örneğin şöyle kullanılabilsin:
void main() { OyunKağıdı[] deste = yeniDeste(); foreach (kağıt; deste) { oyunKağıdıYazdır(kağıt); dout.writef(' '); } dout.writefln(); }
Eğer destedeki her kağıt gerçekten farklı olmuşsa, şuna benzer bir çıktı olmalıdır:
♠2 ♠3 ♠4 ♠5 ♠6 ♠7 ♠8 ♠9 ♠0 ♠J ♠Q ♠K ♠A ♡2 ♡3 ♡4 ♡5 ♡6 ♡7 ♡8 ♡9 ♡0 ♡J ♡Q ♡K ♡A ♢2 ♢3 ♢4 ♢5 ♢6 ♢7 ♢8 ♢9 ♢0 ♢J ♢Q ♢K ♢A ♣2 ♣3 ♣4 ♣5 ♣6 ♣7 ♣8 ♣9 ♣0 ♣J ♣Q ♣K ♣A
void karıştır(OyunKağıdı[] deste, in int değişTokuşAdedi) { // ... burasını siz yazın }
Şu şekilde çağrılabilsin:
void main() { OyunKağıdı[] deste = yeniDeste(); karıştır(deste, 1); foreach (kağıt; deste) { oyunKağıdıYazdır(kağıt); dout.writef(' '); } dout.writefln(); }
değişTokuşAdedi ile belirtilen değer kadar değiş tokuş işlemi gerçekleştirsin. Örneğin 1 ile çağrıldığında şuna benzer bir çıktı versin:
♠2 ♠3 ♠4 ♠5 ♠6 ♠7 ♠8 ♠9 ♠0 ♠J ♠Q ♠K ♠A ♡2 ♡3 ♡4 ♡5 ♡6 ♡7 ♡8 ♣4 ♡0 ♡J ♡Q ♡K ♡A ♢2 ♢3 ♢4 ♢5 ♢6 ♢7 ♢8 ♢9 ♢0 ♢J ♢Q ♢K ♢A ♣2 ♣3 ♡9 ♣5 ♣6 ♣7 ♣8 ♣9 ♣0 ♣J ♣Q ♣K ♣A
değişTokuşAdedi olarak daha yüksek bir değer verdiğinizde deste iyice karışmış olmalıdır:
karıştır(deste, 100);
♠4 ♣7 ♢9 ♢6 ♡2 ♠6 ♣6 ♢A ♣5 ♢8 ♢3 ♡Q ♢J ♣K ♣8 ♣4 ♡J ♣Q ♠Q ♠9 ♢0 ♡A ♠A ♡9 ♠7 ♡3 ♢K ♢2 ♡0 ♠J ♢7 ♡7 ♠8 ♡4 ♣J ♢4 ♣0 ♡6 ♢5 ♡5 ♡K ♠3 ♢Q ♠2 ♠5 ♣2 ♡8 ♣A ♠K ♣9 ♠0 ♣3
D.ershane
Forum
Wiki
Projeler
Tanıtım
İletişim
Hakları