İşleç Yükleme
Not: D'de işleç yükleme olanağı, bu ders yazıldıktan sonra değişti. Aşağıda anlatılanların bir bölümü yakında emekliye ayrılacak. Bu dersi, yeni işleç yükleme olanağını içerecek şekilde değiştireceğim.
Bu derste yapılar üzerinde anlatılanlar daha sonra göreceğimiz sınıflar için de hemen hemen aynen geçerlidir. En belirgin fark, özel işlevler dersinde gördüğümüz atama işleci opAssign'ın sınıflar için tanımlanamıyor olmasıdır.
İşleç yükleme, işleçlerin kendi türlerimizle nasıl çalışacaklarını belirleme olanağıdır.
Yapıların ve onlar için özel olarak tanımlayabildiğimiz üye işlevlerin yararlarını önceki derslerde gördük. Bunun bir örneği, GününSaati nesnelerine Süre nesnelerini ekleyebilmekti. Kodu kısa tutmak için yalnızca bu dersi ilgilendiren üye işlevi göstererek:
struct Süre { int dakika; } struct GününSaati { int saat; int dakika; void ekle(in Süre süre) { dakika += süre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24; } } void main() { auto yemekZamanı = GününSaati(12, 0); yemekZamanı.ekle(Süre(10)); }
Üye işlevlerin yararı, yapı ile ilgili işlemlerin yapı tanımı ile aynı yerde yapılabilmesidir. Üyeler ve işlevler bir arada olunca, yapının üyelerinin bütün işlevler tarafından doğru olarak kullanıldıkları da hep birden denetlenebilir.
Bütün bu yararlarına karşın, temel türlerle karşılaştırıldıklarında yapıların bir yetersizlikleri ortaya çıkar: Temel türler, özel hiçbir şey yapmaya gerek olmadan işleçlerle rahatça kullanılabilirler:
int ağırlık = 50; ağırlık += 10; // işleçle
Şimdiye kadar öğrendiğimiz kadarıyla, benzer bir işlemi yapılar için gerçekleştirmek için bir üye işlev kullanmamız gerekir:
auto yemekZamanı = GününSaati(12, 0); yemekZamanı.ekle(Süre(10)); // üye işlevle
İşleç yükleme olanağı bu konuda yarar sağlar: yapıları da temel türler gibi işleçlerle kullanabilme olanağı sunar. Örneğin GününSaati yapısı için tanımlayabileceğimiz += işleci, yukarıdaki işlemi temel türlerde olduğu gibi daha doğal olarak yazmamızı sağlar:
yemekZamanı += Süre(10); // yapı için de işleçle
Yüklenebilecek bütün işleçlere geçmeden önce bu kullanımın nasıl sağlandığını göstermek istiyorum. Daha önce ismini ekle olarak yazdığımız işlevi, D'nin özel olarak belirlediği opAddAssign ismiyle tanımlamak yeterlidir:
struct GününSaati { // ... void opAddAssign(in Süre süre) { dakika += süre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24; } }
Derleyici, GününSaati nesnelerinin += işleciyle kullanıldığı yerlerde perde arkasında opAddAssign işlevini çağırır:
yemekZamanı += Süre(10);
yemekZamanı.opAddAssign(Süre(10)); // üsttekinin eşdeğeri
Uyarı
İşleçlerin davranışlarını bizim belirleyebiliyor olmamız, bize çoğu işleç için istediğimiz şeyi yapma serbestisi verir. Örneğin yukarıdaki işlevi süre ekleyecek şekilde değil, tam tersine süre azaltacak şekilde de yazabilirdik. Oysa kodu okuyanlar += işlecini gördüklerinde doğal olarak değerin artması gibi bir davranış bekleyeceklerdir.
İşleçleri doğal davranışları dışında yazdığınızda, bunun sizin ve başka programcıların kafalarını karıştıracağını ve programda hatalara neden olacağını aklınızda tutun.
Tekli işleç
Yalnızca tek bir nesneyle çalışan işleçlere tekli işleç denir:
++ağırlık;
++ işleci tekli işleçtir, çünkü işini yaparken yalnızca ağırlık'ı kullanır ve onun değerini bir arttırır.
İkili işleç
İki nesne kullanan işleçlere ikili işleç denir:
toplamAğırlık = kutuAğırlığı + çikolataAğırlığı;
Yukarıdaki ifadede iki farklı ikili işleç görüyoruz: + işleci, kendisinin solundaki ve sağındaki değerleri toplar; = işleci ise sağındakinin değerini solundakine atar.
Bu derste yüklenebilen bütün işleçler için örnekler vermeyeceğim; yalnızca çok kullanıldığını düşündüğüm işleçleri anlatacağım. Zaten bu işleçlerin bazılarını yüklemeye belki de hiç ihtiyacınız olmayacak.
Yüklenebilen tekli işleçler
| İşleç | Anlamı | İşlev İsmi |
|---|---|---|
| -nesne | ters işaretlisini üret | opNeg |
| +nesne | aynı işaretlisini üret | opPos |
| ~nesne | bit düzeyinde tersini al | opCom |
| *nesne | gösterdiğine eriş | opStar |
| ++nesne | bir arttır | bkz. += 1 |
| nesne++ | önceki değerle bir arttır | opPostInc |
| --nesne | bir azalt | bkz. -= 1 |
| nesne-- | önceki değerle bir azalt | opPostDec |
| cast(Tür)nesne | tür dönüştür | opCast |
Yukarıdaki işlev isimleri, işlecin yüklenmesi için hangi isimde üye işlev tanımlanması gerektiğini belirtir. Derleyici, işleç kullanımı yerine bu üye işlevi çağırır. Bunu, önceki derste gördüğümüz opAssign üye işlevi ile şöyle gösterebiliriz:
zaman = başkaZaman;
zaman.opAssign(başkaZaman); // üsttekinin eşdeğeri
++nesne şeklindeki kullanım yüklenemez. Onun yerine, aşağıda gösterilen += işleci yüklenir ve derleyici o işlevi 1 değeriyle çağırır. Benzer şekilde, --nesne kullanımı için de -= işleci 1 değeriyle çağrılır.
Elle açıkça yapılan tür dönüşümünü belirleyen opCast, tek bir tür için yüklenebilir ve o tür opCast'in dönüş türü olarak yazılır. Örneğin Süre nesnelerini int türüne dönüştürmek için:
struct Süre { int dakika; int opCast() { return dakika; } }
Açıkça tür dönüşümü istendiğinde:
auto süre = Süre(42); auto tamsayıOlarak = cast(int)süre;
Derleyici son satır için üye işlevi şöyle çağırır:
auto tamsayıOlarak = süre.opCast();
Yüklenebilen ikili işleçler
Aşağıdaki tabloda gösterilen değişmeli kavramı, işlecin sol tarafındaki ile sağ tarafındakinin yer değiştirmesi durumunda sonucun aynı olup olmadığını gösterir. Örneğin + işleci değişmeli bir işleçtir, çünkü x+y ile y+x işlemlerinin sonuçları aynıdır. Öte yandan, - değişmeli bir işleç değildir, çünkü x-y ve y-x işlemlerinin sonuçları farklıdır.
İşleçleri gruplandırmak için aşağıdaki tabloda işleçlerin türlerini de belirttim:
- a: aritmetik işleç
- b: bit düzeyinde işlem işleci
- m: mantıksal işleç;
booldöndürür - s: sıralama işleci; -1, 0, veya 1 döndürür
- =: atamalı işleç; sol tarafı değiştirir
İşleç | Anlamı | Değişmeli (Komütatif) | İşlev İsmi | Sağ Taraf için İşlev İsmi | Tür |
|---|---|---|---|---|---|
| + | topla | evet | opAdd | opAdd_r | a |
| - | çıkar | hayır | opSub | opSub_r | a |
| * | çarp | evet | opMul | opMul_r | a |
| / | böl | hayır | opDiv | opDiv_r | a |
| % | kalanını hesapla | hayır | opMod | opMod_r | a |
| ^^ | üssünü al | hayır | opPow | opPow_r | a |
| & | bit işlemi ve | evet | opAnd | opAnd_r | b |
| | | bit işlemi veya | evet | opOr | opOr_r | b |
| ^ | bit işlemi ya da | evet | opXor | opXor_r | b |
| << | sola kaydır | hayır | opShl | opShl_r | b |
| >> | sağa kaydır | hayır | opShr | opShr_r | b |
| >>> | işaretsiz sağa kaydır | hayır | opUShr | opUShr_r | b |
| ~ | birleştir | hayır | opCat | opCat_r | |
| == | eşittir | evet | opEquals | - | m |
| != | eşit değildir | evet | opEquals | - | m |
| < | öncedir | evet | opCmp | - | s |
| <= | sonra değildir | evet | opCmp | - | s |
| > | sonradır | evet | opCmp | - | s |
| >= | önce değildir | evet | opCmp | - | s |
| = | ata | hayır | opAssign | - | = |
| += | arttır | hayır | opAddAssign | - | = |
| -= | azalt | hayır | opSubAssign | - | = |
| *= | katını al | hayır | opMulAssign | - | = |
| /= | kadarına böl | hayır | opDivAssign | - | = |
| %= | kalanını ata | hayır | opModAssign | - | = |
| &= | & sonucunu ata | hayır | opAndAssign | - | = |
| |= | | sonucunu ata | hayır | opOrAssign | - | = |
| ^= | ^ sonucunu ata | hayır | opXorAssign | - | = |
| <<= | << sonucunu ata | hayır | opShlAssign | - | = |
| >>= | >> sonucunu ata | hayır | opShrAssign | - | = |
| >>>= | >>> sonucunu ata | hayır | opUShrAssign | - | = |
| ~= | sonuna ekle | hayır | opCatAssign | - | = |
| in | içinde mi? | hayır | opIn | opIn_r | m |
Tabloda sağ taraf için olarak belirtilen işlev isimleri, nesne işlecin sağ tarafında da kullanılabildiğinde işletilir. Kodda şöyle bir ikili işleç kullanıldığını düşünelim:
x işleç y
Derleyici, hangi üye işlevi işleteceğine karar vermek için, yukarıdaki ifadeyi şu iki üye işlev çağrısına dönüştürür:
x.opİşlevİsmi(y);
y.opİşlevİsmi_r(x);
Eğer o işlevlerden birisi tanımlanmışsa, o işletilir. Değilse, ve işleç değişmeli bir işleçse, ayrıca şu işlevleri de dener:
x.opİşlevİsmi_r(y);
y.opİşlevİsmi(x);
Normalde bu kadar karmaşık kuralları bilmek zorunda kalmayız. Çoğu durumda bu işlevlerden ismi _r ile sonlananların hiç tanımlanmasına gerek yoktur.
Aşağıdaki örneklerde üye işlevleri tanımlarken parametre ismini sağdaki olarak seçtim. Bununla, parametrenin işlecin sağındaki nesne olduğunu vurguluyorum:
x işleç y
O ifade kullanıldığında, üye işlevin sağdaki ismindeki parametresi y olacaktır.
Aritmetik işleçler
Kendi türlerimizin aritmetik işlemlerde nasıl kullanıldıklarını belirler. Örneğin Süre nesnelerini birbirleriyle toplamak için opAdd işlecini şöyle yükleyebiliriz:
struct Süre { int dakika; Süre opAdd(in Süre sağdaki) const { return Süre(dakika + sağdaki.dakika); } }
O tanımdan sonra programlarımızda artık Süre nesnelerini + işleciyle toplayabiliriz:
auto gitmeSüresi = Süre(10); auto dönmeSüresi = Süre(11); Süre toplamSüre; // ... toplamSüre = gitmeSüresi + dönmeSüresi;
Derleyici sağdaki ifadeyi dönüştürür ve perde arkasında gitmeSüresi nesnesi üzerinde bir üye işlev olarak çağırır:
toplamSüre = gitmeSüresi.opAdd(dönmeSüresi);
Eğer Süre.opAssign işlevini de tanımlamış olsaydık, bütün ifade şuna dönüşürdü:
toplamSüre.opAssign(gitmeSüresi.opAdd(dönmeSüresi));
Eşitlik karşılaştırmaları için opEquals
== ve != işleçlerinin davranışını belirler.
Mantıksal ifadelerde kullanıldığı için dönüş türü bool'dur.
opEquals üye işlevi, bu işleçlerin ikisini de karşılar. O işlevi nesnelerin eşitliği için tanımlayınca, derleyici != işleci için onun tersini kullanır:
x == y;
x.opEquals(y); // üsttekinin eşdeğeri
x != y;
!(x.opEquals(y)); // üsttekinin eşdeğeri
Normalde opEquals işlevini yapılar için tanımlamaya gerek yoktur; derleyici bütün üyelerin eşitliklerini sırayla otomatik olarak karşılaştırır ve nesnelerin eşit olup olmadıklarına öyle karar verir.
Bazen nesnelerin eşitliklerini kendimiz belirlemek isteyebiliriz. Örneğin bazı üyeler nesnenin eşit kabul edilmesi için önemsiz olabilirler; bir türün nesnelerinin eşit kabul edilmeleri özel bir kurala bağlı olabilir; vs.
Bunu göstermek için GününSaati sınıfı için dakika bilgisini gözardı eden bir opEquals tanımlayalım:
struct GününSaati { int saat; int dakika; bool opEquals(const ref GününSaati sağdaki) const { return saat == sağdaki.saat; } } // ... assert(GününSaati(20, 10) == GününSaati(20, 59));
Eşitlik karşılaştırmasında yalnızca saat bilgisine bakıldığı için 20:10 ve 20:59 zamanları eşit çıkmaktadır.
Sıra karşılaştırmaları için opCmp
Sıralama işleçleri nesnelerin öncelik/sonralık ilişkilerini belirler. Sıralama ile ilgili olan <, <=, >, ve >= işleçlerinin hepsi opCmp üye işlevi tarafından karşılanır.
Bu dört işleçten birisinin şu şekilde kullanıldığını kabul edelim:
if (x işleç y) {
Derleyici o ifadeyi aşağıdakine dönüştürür ve o mantıksal ifadenin sonucunu kullanır:
if (x.opCmp(y) işleç 0) {
Örnek olarak,
if (x <= y) {
ifadesi şuna dönüştürülür:
if (x.opCmp(y) <= 0) {
Kendi yazdığımız bu işlevin bu kurala göre doğru çalışabilmesi için işlevin şu değerleri döndürmesi gerekir:
- soldaki nesne önce olduğunda eksi bir değer
- sağdaki nesne önce olduğunda artı bir değer
- ikisi eşit olduklarında sıfır değeri
Bundan anlaşılacağı gibi, opCmp'ın dönüş türü bool değil, int'tir.
GününSaati nesnelerinin sıralama ilişkilerini; öncelikle saat değerine, saatleri eşit olduğunda da dakika değerlerine bakacak şekilde şöyle belirleyebiliriz:
int opCmp(const ref GününSaati sağdaki) const { return (saat == sağdaki.saat ? dakika - sağdaki.dakika : saat - sağdaki.saat); }
Saat değerleri aynı olduğunda dakika değerlerinin farkı, saatler farklı olduğunda da saatlerin farkı döndürülüyor. Bu tanım, zaman sıralamasında soldaki nesne önce olduğunda eksi, sağdaki nesne önce olduğunda artı bir değer döndürür.
opCmp'un tanımlanmış olması, türümüzün sıralama algoritmalarıyla da kullanılabilmelerini sağlar. Sıralama algoritmalarının bir örneğini dizilerin .sort niteliğinde görmüştük. Aşağıdaki program 10 adet rastgele zaman değeri oluşturur ve onları dizinin .sort niteliği ile sıralar:
import std.random; import std.cstream; import std.string; struct GününSaati { int saat; int dakika; int opCmp(const ref GününSaati sağdaki) const { return (saat == sağdaki.saat ? dakika - sağdaki.dakika : saat - sağdaki.saat); } string toString() { return format("%02s:%02s", saat, dakika); } } void main() { GününSaati[] zamanlar; foreach (i; 0 .. 10) { zamanlar ~= GününSaati(uniform(0, 24), uniform(0, 60)); } zamanlar.sort; dout.writefln(zamanlar); }
Dizinin .sort niteliği, bizim tanımladığımız sıralama ilişkisini kullanır. Beklendiği gibi, çıktıdaki saat değerleri doğru sıradadır:
[03:40,04:10,09:06,10:03,10:09,11:04,13:42,16:40,18:03,21:08]
İşlev gibi çağırmak için opCall
İşlev çağırırken kullanılan parantezler de işleçtir. Bu işlecin davranışı da opCall üye işlevi tarafından belirlenir. Bu işleç sayesinde türün nesnelerini de işlev gibi kullanabiliriz:
BirTür nesne;
nesne();
O kodda nesne bir işlev gibi çağrılmaktadır.
Bunun bir örneği olarak; bir doğrusal denklemde, verilen x değerlerine karşılık y değerlerini hesaplayan bir yapı düşünelim:
y = ax + b
O hesaptaki a ve b'yi çarpan ve eklenen isimli üyeler olarak düşünürsek, y değerlerini opCall işlevi içinde şöyle hesaplayabiliriz:
struct DoğrusalDenklem { double çarpan; double eklenen; double opCall(double x) { return çarpan * x + eklenen; } }
Bir kere bunu tanımladıktan sonra, o sınıfın nesnelerini bir işlev gibi kullanarak verilen x değerlerine karşılık olan y değerlerini hesaplatabiliriz:
DoğrusalDenklem denklem = { 1.2, 3.4 };
double y = denklem(5.6); // nesne işlev gibi kullanılıyor
Not: opCall işlevi tanımlanmış olan yapıları Tür(parametreler) yazımıyla kuramayız; çünkü o yazım da bir opCall çağrısı olarak kabul edilir. opCall'u tanımlanmış olan yapıların nesnelerini { } yazımıyla kurmamız gerekir.
İlk satırda nesne kurulurken denklemin çarpanı olarak 1.2, ekleneni olarak da 3.4 değerinin kullanılacağı belirleniyor. Ondan sonra nesneyi artık bir işlev gibi kullanarak, x değerlerini parametre olarak gönderiyor ve dönüş değeri olarak y değerlerini elde ediyoruz.
Bunun önemi, çarpan ve eklenen değerlerin baştan tek bir kere belirlenebilmesidir. Nesne o bilgiyi kendi içinde barındırır ve işlev gibi kullanıldığı zaman kullanır.
Başka çarpan ve eklenen değerleri ile kurulan bir nesneyi, bu sefer de bir döngü içinde kullanan bir örnek:
DoğrusalDenklem denklem = { 0.01, 0.4 };
for (double x = 0.0; x <= 1.0; x += 0.125) {
dout.writefln("%f: %f", x, denklem(x));
}
Dizi erişim işleçleri opIndex ve opIndexAssign
Bu işleçler, nesneyi nesne[indeks] şeklinde dizi gibi kullanma olanağı sağlarlar.
opIndex erişim amacıyla kullanılır. Köşeli parantezler içinde kullanılan değerler işlevin parametreleri haline gelirler:
birNot = bütünNotlar[3, 1];
birNot = bütünNotlar.opIndex(3, 1); // üsttekinin eşdeğeri
opIndexAssign ise atama amacıyla kullanılabilir. İlk parametresi atanan değer, sonraki parametreleri de köşeli parantezler içinde kullanılan değerlerdir:
bütünNotlar[1, 1] = 95;
bütünNotlar.opIndexAssign(95, 1, 1);// üsttekinin eşdeğeri
Bir örnek olarak, bütün öğrencilerin bütün notlarını içeren bir yapıyı şöyle tanımlayabiliriz:
import std.cstream; struct BütünÖğrencilerinNotları { // 5 öğrenci için 2 not int[2][5] notlar; int opIndex(int öğrenciİndeksi, int notİndeksi) { return notlar[öğrenciİndeksi][notİndeksi]; } int opIndexAssign(int not, int öğrenciİndeksi, int notİndeksi) { return notlar[öğrenciİndeksi][notİndeksi] = not; } } void main() { BütünÖğrencilerinNotları bütünNotlar; int birNot = bütünNotlar[3, 1]; // erişim bütünNotlar[1, 1] = 95; // atama dout.writefln(bütünNotlar.notlar); }
Erişim satırını yalnızca göstermek amacıyla kullandım; programda bir amacı bulunmuyor. Atama yapan satır ise 1 indeksli öğrencinin 1 indeksli notunu değiştirmektedir:
[[0,0],[0,95],[0,0],[0,0],[0,0]]
Dilim işleçleri opSlice ve opSliceAssign
Nesneleri dilim işleciyle kullanma olanağı sağlarlar. İkisinin de iki farklı kullanımı vardır: köşeli parantezlerin içinin boş olduğu kullanım, ve köşeli parantezler içinde bir aralık belirtilen kullanım.
Bu dört kullanımın hangi işlevler tarafından yüklendiğini, bu işlevleri tanımlamadan gösteren bir örnek:
class BirTür { int opSlice(); // nesne[] kullanımı int opSlice(int x, int y); // nesne[i .. j] kullanımı int opSliceAssign(int değer); // nesne[] = değer kullanımı int opSliceAssign(int değer, int x, int y); // nesne[i .. j] = değer kullanımı } void main() { BirTür nesne; int i; int değer; i = nesne[]; i = nesne.opSlice(); // üsttekinin eşdeğeri i = nesne[3 .. 4]; i = nesne.opSlice(3, 4); // üsttekinin eşdeğeri nesne[] = değer; nesne.opSliceAssign(değer); // üsttekinin eşdeğeri nesne[3 .. 4] = değer; nesne.opSliceAssign(değer, 3, 4); // üsttekinin eşdeğeri }
Sevk işleci opDispatch
Nesnenin var olmayan bir üyesine erişildiğinde çağrılacak olan üye işlevdir. Var olmayan üyelere erişim, bu işlece sevk edilir.
Var olmayan üyenin ismi, opDispatch'e bir şablon parametresi olarak gelir. (Not: Şablonları daha sonraki bir derste anlatacağım.)
Bu işleci çok basit olarak gösteren bir örnek:
import std.cstream; struct BirTür { void opDispatch(string isim, T)(T parametre) { dout.writefln( "BirTür.opDispatch - isim: %s, değer: %s", isim, parametre); } } void main() { BirTür nesne; nesne.varOlmayanİşlev(42); nesne.varOlmayanBaşkaİşlev(100); }
Var olmayan üyelerine eriştiğimiz halde derleme hatası almayız. O çağrılar, opDispatch işlevinin çağrılmasını sağlarlar. Birinci şablon parametresi işlevin ismidir. Çağrılan noktada kullanılan parametreler de opDispatch'in parametreleri haline gelirler:
BirTür.opDispatch - isim: varOlmayanİşlev, değer: 42 BirTür.opDispatch - isim: varOlmayanBaşkaİşlev, değer: 100
İçerme sorgusu için opIn
Eşleme tablolarından tanıdığımız in işlecini nesneler için de tanımlama olanağı sağlar.
Diğer işleçlerden farklı olarak, bu işleçte nesnenin sağda yazıldığı durum daha doğaldır:
if (zaman in öğleTatili) {
O yüzden bu işleç için daha çok opIn_r yüklenir ve derleyici de perde arkasında o üye işlevi çağırır:
if (öğleTatili.opIn_r(zaman)) { // üsttekinin eşdeğeri
Bu işleci aşağıdaki örnekte kullanıyorum.
Örnek
Bu örnek, daha önce gördüğümüz Süre ve GününSaati yapılarına ek olarak bir ZamanAralığı yapısı tanımlıyor. Bu yapı için tanımlanan in işleci, belirli bir zamanın belirli bir aralıkta olup olmadığını belirtiyor.
Bu örnekte de yalnızca gerektiği kadar üye işlev kullandım.
GününSaati nesnesinin for döngüsünde nasıl temel türler kadar rahat kullanıldığına özellikle dikkat edin. O döngü, işleç yüklemenin yararını güzelce gösteriyor.
import std.cstream; import std.string; struct Süre { int dakika; } struct GününSaati { int saat; int dakika; void opAddAssign(in Süre süre) { dakika += süre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24; } int opCmp(const ref GününSaati sağdaki) const { return (saat == sağdaki.saat ? dakika - sağdaki.dakika : saat - sağdaki.saat); } string toString() { return format("%02s:%02s", saat, dakika); } } struct ZamanAralığı { GününSaati baş; GününSaati son; // aralığın dışında kabul edilir bool opIn_r(const ref GününSaati zaman) const { return (zaman >= baş) && (zaman < son); } } void main() { auto öğleTatili = ZamanAralığı(GününSaati(12, 00), GününSaati(13, 00)); for (auto zaman = GününSaati(11, 30); zaman < GününSaati(13, 30); zaman += Süre(15)) { if (zaman in öğleTatili) { dout.writefln(zaman, " öğle tatilinde"); } else { dout.writefln(zaman, " öğle tatili dışında"); } } }
Programın çıktısı:
11:30 öğle tatili dışında 11:45 öğle tatili dışında 12:00 öğle tatilinde 12:15 öğle tatilinde 12:30 öğle tatilinde 12:45 öğle tatilinde 13:00 öğle tatili dışında 13:15 öğle tatili dışında
Problemler
GününSaatiyapısınınopCmpişlevini zamanların ters sırada sıralanmalarını sağlayacak şekilde değiştirin.- Yukarıda kullanılan
ZamanAralığıyapısı için>>=ve<<=işleçlerini tanımlayın. Normalde bit işlemleri için kullanılan bu işleçler, zaman aralığını belirli birSürekadar sağa ve sola kaydırsınlar: - Bu kullanımı şüpheli olsa da,
~işleciniZamanAralığıveGününSaatinesnelerinin aşağıdaki iki kullanımları için yükleyin:
aralık >>= Süre(10); // bütün aralık 10 dakika // sonrasına kaysın
<<= işleci için Üye İşlevler dersinin çözümlerinde gördüğümüz GününSaati.azalt işlevinden yararlanmak isteyebilirsiniz. Onun ismini değiştirerek GününSaati nesneleri için -= işleci olarak kullanabilirsiniz.
ZamanAralığı aralık;
GününSaati zaman;
// ...
yeniAralık = aralık ~ zaman;
şeklindeki kullanımda yeni aralığın sonu zaman'a eşit olsun, ve
yeniAralık = zaman ~ aralık;
şeklindeki kullanımda da yeni aralığın başı zaman'a eşit olsun.
D.ershane
Forum
Wiki
Projeler
Tanıtım
İletişim
Hakları