Değişmezlik
Kavramlar, programlarda kullanılan değişkenlerle temsil edilir. Kavramlar arasındaki etkileşimleri bu değişkenlerin değerlerini değiştirerek sağlarız. Örneğin, aşağıdaki kod bir alışveriş ile ilgili değişkenlerin değerlerini değiştirmektedir:
toplamFiyat = fiyatıHesapla(fiyatListesi); cüzdandakiMiktar -= toplamFiyat; bakkaldakiMiktar += toplamFiyat;
Değer değişiminin bilerek kısıtlanmasına değişmezlik denir.
Değer değişimi çoğu iş için gerekli olduğundan değişimin bilerek kısıtlanması anlamsız gibi görünebilse de oldukça güçlü ve yararlı bir olanaktır. Değişmezlik kavramı, yazılım dünyası tarafından edinilmiş olan deneyimlere dayanır: Değişmezlik, kodların doğruluğuna ve kolay değiştirilebilmelerine katkı sağlar. Hatta, bazı fonksiyonel programlama dilleri değer değişimini bütünüyle yasaklarlar.
Değişmezliğin getirdiği bazı yararlar şunlardır:
- Bazı kavramlar zaten değişmezdirler. Örneğin haftadaki gün sayısı 7'dir, matematikteki pi (π) sabittir, bir programın desteklediği dil sayısı programın çalıştığı sürece değişmeyecektir (örneğin yalnızca Türkçe ve İngilizce'dir), vs.
- Değişmeyen kavramları temsil etmeseler bile, bazı değişkenlerin değerlerinin bir kere ilklendikten sonra değişmesi istenmiyer olabilir ve bunların sonradan değişmeleri programcı hatası olarak kabul edilebilir. Örneğin, yukarıdaki koddaki
toplamFiyat
'ın değeri bir kere belirlendikten sonra değişmemelidir. - Koddaki bütün işlemlerin her değişkeni değiştirebilecek kadar esnek olmaları, hangi işlemlerin hangi değişkenleri değiştirdiklerini fazla serbest bıraktığından kodun okunması ve geliştirilmesi güçleşir.
- İşlevler, parametrelerinin yalnızca bazılarını değişebilen türden tanımladıklarında, hangi parametreleri yalnızca giriş bilgisi olarak kullanacaklarını ve hangilerini yan etkileri olarak değiştireceklerini belirtmiş olurlar.
Değişmezlik, hem genel olarak programlamada çok yaygın olduğundan hem de D programcılığında çok kullanıldığından, bu kavramla ilgili aşağıdaki garipliklere göz yumulur:
- Şimdiye kadar yazmış olduğumuz programlardan görüldüğü gibi, değişmezlik kesinlikle gerekli değildir.
- Değişmezlik, D'd
const
("sabit, değişmez" anlamına gelen "constant"ın kısası) veimmutable
("değişebilen" anlamına gelen "mutable"ın karşıt anlamlısı) anahtar sözcükleriyle ifade edilir. Her ne kadar İngilizce'de aynı anlama gelseler de bu anahtar sözcüklerin görevleri farklıdır ve bazı durumlarda birbirleriyle uyumsuzdur. (inout
veshared
gibi,const
veimmutable
da tür nitelendiricisidir.) - Bir değişkenin değişmezliğinden bahseden "constant variable" ve "immutable variable" terimleri İngilizce de anlamsızdır ve kulağa yanlış gelir.
- İşlevler değişmezlik kavramını göz önüne almaya zorlanırlar ve bu sayede daha kullanışlı hale gelirler. (Bu zorundalığın işlevden işleve geçmesi bazen bir virüsün yayılmasına benzetilir ve "const-correctness" olarak adlandırılır.)
Değişmezler
Kesinlikle değişmeyecek olan değişkenler üç farklı biçimde tanımlanabilirler.
enum
değişkenler
Bazı sabit değişkenlerin enum
olarak tanımlanabildiklerini enum
bölümünde görmüştük:
enum dosyaİsmi = "liste.txt";
Derleme zamanında hesaplanabildikleri sürece enum
değişkenler işlev çağrıları da dahil olmak üzere daha karmaşık ifadelerle de ilklenebilirler:
int satırAdedi() { return 42; } int sütunAdedi() { return 7; } string isim() { return "liste"; } void main() { enum dosyaİsmi = isim() ~ ".txt"; enum toplamKare = satırAdedi() * sütunAdedi(); }
Bunu sağlayan D olanağı, ilerideki bir bölümde göreceğimiz derleme zamanında işlev işletme olanağıdır (CTFE).
Derleyici enum
değişkenlerin değiştirilmelerine izin vermez:
++toplamKare; // ← derleme HATASI
Değişmezlik kavramını sağlayan çok etkili bir olanak olmasına karşın enum
ancak değerleri derleme zamanında bilinen veya hesaplanabilen sabitler için kullanılabilir.
Bekleneceği gibi, program derlenirken enum
değişkenlerin yerlerine onların değerleri kullanılır. Örneğin, şöyle bir enum
tanımı ve onu kullanan iki ifade olsun:
enum i = 42;
writeln(i);
foo(i);
Yukarıdaki kod, i
'nin yerine onun değeri olan 42
'nin doğrudan yazılmasının eşdeğeridir:
writeln(42); foo(42);
Bir enum
değişkenin yerine değerinin kullanılıyor olması int
gibi basit türler için normal olarak kabul edilmelidir. Ancak, enum
değişkenlerin dizi veya eşleme tablosu olarak kullanılmalarının gizli bir bedeli vardır:
enum a = [ 42, 100 ];
writeln(a);
foo(a);
a
'nın yerine değerini yerleştirdiğimizde derleyicinin derleyeceği asıl kodun aşağıdaki gibi olduğunu görürüz:
writeln([ 42, 100 ]); // bir dizi oluşturulur foo([ 42, 100 ]); // başka bir dizi oluşturulur
Yukarıdaki koddaki gizli bedel, her ifade için farklı bir dizi oluşturuluyor olmasıdır. Bu yüzden, birden fazla yerde kullanılacak olan dizilerin ve eşleme tablolarının immutable
değişkenler olarak tanımlanmaları çoğu duruma daha uygundur.
const
değişkenler
enum
gibi, bu anahtar sözcük de bir değişkenin değerinin değişmeyeceğini bildirir. enum
'dan farkı, const
değişkenlerin adresleri olan normal değişkenler olmaları ve ilk değerlerini çalışma zamanında da alabilmeleridir.
Derleyici const
değişkenlerin değiştirilmelerine izin vermez:
const yarısı = toplam / 2; yarısı = 10; // ← derleme HATASI
Aşağıdaki program enum
ve const
anahtar sözcüklerinin kullanımlarının farklarını gösteriyor. Tuttuğu sayıyı kullanıcının tahmin etmesini bekleyen bu programda tutulan sayı derleme zamanında bilinemeyeceğinden enum
olarak tanımlanamaz. Ancak, bir kere seçildikten sonra değerinin değişmesi istenmeyeceğinden ve hatta değişmesi bir hata olarak kabul edileceğinden bu değişkenin const
olarak işaretlenmesi uygun olur.
Aşağıdaki program kullanıcının tahminini okurken yine bir önceki bölümde tanımladığımız sayıOku
işlevinden yararlanıyor:
import std.stdio; import std.random; int sayıOku(string mesaj) { int sayı; write(mesaj, "? "); readf(" %s", &sayı); return sayı; } void main() { enum enAz = 1; enum enÇok = 10; const sayı = uniform(enAz, enÇok + 1); writefln("%s ile %s arasında bir sayı tuttum.", enAz, enÇok); auto doğru_mu = false; while (!doğru_mu) { const tahmin = sayıOku("Tahmininiz"); doğru_mu = (tahmin == sayı); } writeln("Doğru!"); }
Gözlemler:
enAz
'ın veenÇok
'un değerleri programın derlenmesi sırasında bilindiklerinden ve bir anlamda bu programın davranışının değişmez parçaları olduklarındanenum
olarak tanımlanmışlardır.- Rasgele seçilmiş olan
sayı
değerinin ve kullanıcıdan okunan hertahmin
değerinin programın işleyişi sırasında değişmeleri doğru olmayacağından onlarconst
olarak tanımlanmışlardır. - O değişkenlerin tanımları sırasında türlerinin açıkça belirtilmediğine dikkat edin.
auto
'da olduğu gibi,enum
veconst
anahtar sözcükleri de türün sağ tarafın değerinden çıkarsanması için yeterlidir.
Program içinde açıkça const(int)
diye parantezle yazılması gerekmese de const
türün bir parçasıdır. Aşağıdaki program üç farklı biçimde tanımlanmış olan değişkenlerin türlerinin tam isimlerinin aynı olduklarını gösteriyor:
import std.stdio; void main() { const çıkarsanarak = 0; const int türüyle = 1; const(int) tamOlarak = 2; writeln(typeof(çıkarsanarak).stringof); writeln(typeof(türüyle).stringof); writeln(typeof(tamOlarak).stringof); }
Üçünün de asıl tür ismi const
'ı da içerir ve parantezlidir:
const(int) const(int) const(int)
Parantezlerin içindeki tür önemlidir. Bunu aşağıda dilimin veya elemanlarının değişmezliği konusunda göreceğiz.
immutable
değişkenler
Değişken tanımında immutable
anahtar sözcüğü const
ile aynıdır. immutable
değişkenler değiştirilemezler:
immutable yarısı = toplam / 2; yarısı = 10; // ← derleme HATASI
Programın başka tarafları özellikle immutable
gerektirmediğinde, değişkenleri const
veya immutable
olarak tanımlayabilirsiniz. Bir işlevin özellikle immutable
gerektirdiği durumda ise o parametreye gönderilecek olan değişkenin de immutable
olarak tanımlanmış olması gerekir. Bunu aşağıda göreceğiz.
Parametreler
Sonraki iki bölümde göreceğimiz gibi, işlevler parametrelerinde değişiklik yapabilirler. Örneğin, parametre olarak gönderilmiş olan dilimlerin elemanlarını değiştirebilirler.
Başka Dizi Olanakları bölümünden hatırlayacağınız gibi, dilimler kendi elemanlarına sahip değillerdir, o elemanlara yalnızca erişim sağlarlar. Belirli bir anda aynı elemana erişim sağlamakta olan birden fazla dilim bulunabilir.
Bu başlık altındaki örneklerde dilimlerden yararlanıyor olsam da burada anlatılanlar eşleme tabloları için de geçerlidir çünkü onlar da referans türleridir.
İşlev parametresi olan bir dilim, işlevin çağrıldığı yerdeki dilimin kendisi değil, bir kopyasıdır. (Yalnızca dilim değişken kopyalanır, elemanları değil.)
import std.stdio; void main() { int[] dilim = [ 10, 20, 30, 40 ]; // 1 yarıla(dilim); writeln(dilim); } void yarıla(int[] sayılar) { // 2 foreach (ref sayı; sayılar) { sayı /= 2; } }
Yukarıdaki yarıla
işlevinin işletildiği sırada aynı dört elemana erişim sağlamakta olan iki farklı dilim vardır:
main
'in içinde tanımlanmış olan veyarıla
'ya parametre olarak gönderilendilim
isimli dilimyarıla
'nın parametre değeri olarak almış olduğu vemain
'deki dilimle aynı dört elemana erişim sağlamakta olansayılar
isimli dilim
foreach
döngüsünde ref
anahtar sözcüğü de kullanılmış olduğundan o dört elemanın değerleri yarılanmış olur:
[5, 10, 15, 20]
Bu örnekte de görüldüğü gibi, yarıla
gibi işlevlerin kendilerine gönderilen dilimlerin elemanlarını değiştirebilmeleri kullanışlıdır çünkü zaten eleman değiştirmek için yazılmışlardır.
Derleyici, const
değişkenlerin böyle işlevlere gönderilmelerine izin vermez:
const int[] dilim = [ 10, 20, 30, 40 ]; yarıla(dilim); // ← derleme HATASI
Derleme hatası, const(int[])
türündeki bir değişkenin int[]
türündeki bir parametre değeri olarak kullanılamayacağını bildirir:
Error: function deneme.yarıla (int[] sayılar) is not callable using argument types (const(int[]))
const
parametreler
const
değişkenlerin yarıla
'da olduğu gibi parametrelerinde değişiklik yapan işlevlere gönderilmelerinin engellenmesi önemlidir. Ancak, parametrelerinde değişiklik yapmayan ve hatta yapmaması gereken aşağıdaki yazdır
gibi işlevlere gönderilememeleri büyük bir kısıtlama olarak görülmelidir:
import std.stdio; void main() { const(int[]) dilim = [ 10, 20, 30, 40 ]; yazdır(dilim); // ← derleme HATASI } void yazdır(int[] dilim) { writefln("%s eleman: ", dilim.length); foreach (i, eleman; dilim) { writefln("%s: %s", i, eleman); } }
Elemanların const
olarak tanımlanmış olmaları, onların yazdırılmalarına engel olmamalıdır. const
parametreler bu konuda yararlıdırlar. (const
kavramını böylece doğru kullandığı düşünülen işlevlerin "const konusunda doğru" anlamında "const-correct" oldukları söylenir.)
const
anahtar sözcüğü bir değişkenin belirli bir referans (örneğin dilim) yoluyla değiştirilmeyeceğini belirler. Parametreyi const
olarak işaretlemek, o dilimin elemanlarının işlev içerisinde değiştirilemeyeceğini garanti eder. Böyle bir garanti sağlandığı için program artık derlenir:
yazdır(dilim); // şimdi derlenir // ... void yazdır(const int[] dilim) { // ... }
İşlev nasıl olsa değiştirmeyeceğine söz vermiş olduğundan, hem değişebilen, hem const
, hem de immutable
değişkenler o işlevin const
parametresi olarak gönderilebilirler:
int[] değişebilenDilim = [ 7, 8 ]; yazdır(değişebilenDilim); // derlenir const int[] dilim = [ 10, 20, 30, 40 ]; yazdır(dilim); // derlenir immutable int[] immDilim = [ 1, 2 ]; yazdır(immDilim); // derlenir
İşlev tarafından değiştirilmediği halde const
olarak tanımlanmayan bir parametre, işlevin kullanışlılığını düşürür. Böyle işlevlerin "const-correct" olmadıkları söylenir.
const
parametrelerin başka bir yararı, programcıya verdikleri yararlı bilgidir: Değişkenin işlev tarafından değiştirilmeyeceğini bilmek kodun anlaşılırlığını arttırır.
const
parametrelerin değişebilen, const
, ve immutable
değişkenleri kabul edebilmelerinin ilginç bir etkisi vardır. Bunu aşağıdaki "const
parametre mi, immutable
parametre mi?" başlığı altında göreceğiz.
in
parametreler
Bir sonraki bölümde göreceğimiz gibi, in
hem const
anlamını içerir, hem de -preview=in
derleyici seçeneği ile kullanıldığında daha yararlıdır. Bu yüzden, const
parametreler yerine in
parametreler kullanmanızı öneririm.
immutable
parametreler
Hem değişebilen, hem const
, hem de immutable
değişkenler alabildiklerinden const
parametrelerin esnek olduklarını söyleyebiliriz.
Öte yandan, bir parametrenin immutable
olarak işaretlenmesi, asıl değişkenin de immutable
olması şartını getirir. Bu açıdan bakıldığında immutable
parametreler işlevin çağrıldığı nokta üzerinde kuvvetli bir talepte bulunmaktadırlar:
void birİşlem(immutable int[] dilim) { // ... } void main() { immutable int[] değişmezDilim = [ 1, 2 ]; int[] değişebilenDilim = [ 8, 9 ]; birİşlem(değişmezDilim); // bu derlenir birİşlem(değişebilenDilim); // ← derleme HATASI }
O yüzden immutable
parametreleri ancak gerçekten gereken durumlarda düşünmenizi öneririm. Şimdiye kadar öğrendiklerimiz arasında immutable
parametreler yalnızca dizgi türlerinde üstü kapalı olarak geçerler. Bunu biraz aşağıda göstereceğim.
const
veya immutable
olarak işaretlenmiş olan parametrelerin, işlevin çağrıldığı yerdeki asıl değişkeni değiştirmeme sözü verdiklerini gördük. Bu konu yalnızca referans türünden olan değişkenlerle ilgilidir.
Referans ve değer türlerini bir sonraki bölümde daha ayrıntılı olarak göreceğiz. Bu bölüme kadar gördüğümüz türler arasında dilimler ve eşleme tabloları referans türleri, diğerleri ise değer türleridir.
const
parametre mi, immutable
parametre mi?
Not: in
parametreler const
anlamını içerdiklerinden, bu bölüm in
parametrelerle de ilgilidir.
Yukarıdaki başlıklara bakıldığında esneklik getirdiği için const
belirtecinin yeğlenmesinin doğru olacağı sonucuna varılabilir. Bu her zaman doğru değildir.
Parametre tanımındaki const
belirteci, asıl değişkenin değişebilen mi, const
mı, yoksa immutable
mı olduğu bilgisini işlev içerisinde belirsiz hale getirir. Bunu derleyici de bilemez.
Bunun bir etkisi, const
parametrelerin immutable
parametre alan başka işlevlere doğrudan gönderilemeyecekleridir. Örneğin, değişken main
içinde her ne kadar immutable
olarak tanımlanmış bile olsa, aşağıdaki koddaki foo
işlevi const
parametresini bar
'a gönderemez:
void main() { /* Asıl değişken immutable */ immutable int[] dilim = [ 10, 20, 30, 40 ]; foo(dilim); } /* Daha kullanışlı olabilmek için parametresini const olarak * alan bir işlev. */ void foo(const int[] dilim) { bar(dilim); // ← derleme HATASI } /* Parametresini belki de geçerli bir nedenle immutable olarak * alan bir işlev. */ void bar(immutable int[] dilim) { // ... }
bar
, parametresinin immutable
olmasını şart koşmaktadır. Öte yandan, foo
'nun const
parametresi olan dilim
'in aslında immutable
bir değişkene mi yoksa değişebilen bir değişkene mi bağlı olduğu bilinemez.
Not: Yukarıdaki kullanıma bakıldığında main
içindeki asıl değişkenin immutable
olduğu açıktır. Buna rağmen, derleyici her işlevi ayrı ayrı derlediğinden foo
'nun const
parametresinin işlevin her çağrıldığı noktada aslında immutable
olduğunu bilmesi olanaksızdır. Derleyicinin gözünde dilim
değişebilen de olabilir immutable
da.
Böyle bir durumda bir çözüm, bar
'ı parametrenin değişmez bir kopyası ile çağırmaktır:
void foo(const int[] dilim) { bar(dilim.idup); }
Bu durumda kodun derlenmesi sağlanmış olsa da, asıl değişkenin zaten immutable
olduğu durumda bile kopyasının alınıyor olmasının gereksiz bir bedeli olacaktır.
Bütün bunlara bakıldığında belki de foo
'nun parametresini const
olarak almasının her zaman için doğru olmadığı düşünülebilir. Çünkü parametresini baştan immutable
olarak seçmiş olsa kod kopyaya gerek kalmadan derlenebilir:
void foo(immutable int[] dilim) { // Bu sefer immutable bar(dilim); // Artık kopya gerekmez }
Ancak, bir üstteki başlıkta belirtildiği gibi, asıl değişkenin immutable
olmadığı durumlarda foo
'nun çağrılabilmesi için bu sefer de .idup
ile kopyalanması gerekecekti:
foo(değişebilenDilim.idup);
Görüldüğü gibi, değişmeyecek olan parametrenin türünün const
veya immutable
olarak belirlenmesinin kararı kolay değildir.
İleride göreceğimiz şablonlar bu konuda da yararlıdırlar. Aşağıdaki kodları kitabın bu aşamasında anlamanızı beklemesem de parametrenin değişebilen, const
, veya immutable
olması kararını ortadan kaldırdığını belirtmek istiyorum. Aşağıdaki foo
, yalnızca asıl değişken immutable
olmadığında kopya bedeli öder; immutable
değişkenler kopyalanmazlar:
import std.conv; // ... /* Şablon olduğu için hem değişebilen hem de immutable * değişkenlerle çağrılabilir. */ void foo(T)(T[] dilim) { /* Asıl değişken zaten immutable olduğunda 'to' ile * kopyalamanın bedeli yoktur. */ bar(to!(immutable T[])(dilim)); }
İlkleme
Değişimin engellenmesinin, değişken ilk değerlerinin az da olsa karmaşık ifadelerden oluştuğu durumlarda kısıtlayıcı olduğu düşünülebilir. Örneğin, aşağıdaki meyveler
dizisinin elemanlarının, turunçgilEklensin_mi
değişkeninin değerine göre belirlenmesi istenmiştir, ancak kod dizi const
olduğundan derlenemez:
const meyveler = [ "elma", "armut" ]; if (turunçgilEklensin_mi) { meyveler ~= [ "portakal" ]; // ← derleme HATASI }
Değişkeni örneğin auto
ile tanımlamak kodun derlenmesi için yeterli olsa da, daha iyi bir yöntem, const
'ı kullanmaya devam etmek ama ilkleme kodunu bir işleve taşımaktır:
bool turunçgilEklensin_mi; string[] meyvelerYap() { auto sonuç = [ "elma", "armut" ]; if (turunçgilEklensin_mi) { sonuç ~= [ "portakal" ]; } return sonuç; } void main() { const meyveler = meyvelerYap(); }
sonuç
dizisinin değişebilen türden olduğu halde meyveler
'in istendiği gibi const
olabildiğine dikkat edin. Kodun bir işleve taşınmasının mümkün olmadığı veya güçlük doğurduğu durumlarda isimsiz işlevler kullanılabilir:
const meyveler = { // 'meyvelerYap()' işlevinin içeriğinin aynısı auto sonuç = [ "elma", "armut" ]; if (turunçgilEklensin_mi) { sonuç ~= [ "portakal" ]; } return sonuç; }();
İsimsiz işlev, işaretlenmiş olarak gösterilen küme parantezleri arasında tanımlanmıştır ve sondaki işlev çağrı parantezleriyle işletilmektedir. Sonuçta meyveler
değişkeni, istendiği gibi const
olarak tanımlanabilmiştir.
shared static this
(ve static this
) özel ilkleme bloklarında const
ve immutable
değişkenlere doğrudan değer atanabilir. Bu bloklar özellikle modül düzeyinde (işlevlerin dışında) tanımlanmış olan değişkenlerin ilklenmeleri için kullanılırlar:
immutable int[] i; shared static this() { // 'const' ve 'immutable' modül değişkenleri bu blok içinde // değiştirilebilirler: i ~= 43; // Değişkenler programın geri kalanında yine de 'const' veya // 'immutable' olarak tanımlanmış gibi kullanılırlar. }
shared static this
blokları main
işlevinden önce işletilirler.
Bütün dilime karşılık elemanlarının değişmezliği
const
bir dilimin türünün .stringof
ile const(int[])
olarak yazdırıldığını yukarıda gördük. const
'tan sonra kullanılan parantezlerden anlaşılabileceği gibi, değişmez olan dilimin bütünüdür; o dilimde hiçbir değişiklik yapılamaz. Örneğin dilime eleman eklenemez, dilimden eleman çıkartılamaz, var olan elemanların değerleri değiştirilemez, veya dilimin başka elemanları göstermesi sağlanamaz:
const int[] dilim = [ 1, 2 ]; dilim ~= 3; // ← derleme HATASI dilim[0] = 3; // ← derleme HATASI dilim.length = 1; // ← derleme HATASI const int[] başkaDilim = [ 10, 11 ]; dilim = başkaDilim; // ← derleme HATASI
Değişmezliğin bu derece ileri götürülmesi bazı durumlara uygun değildir. Çoğu durumda önemli olan, yalnızca elemanların değiştirilmeyecekleri güvencesidir. Dilim nasıl olsa elemanlara erişim sağlayan bir olanak olduğundan o elemanlar değiştirilmedikleri sürece dilimin kendisinde oluşan değişiklikler bazı durumlarda önemli değildir.
Bir dilimin yalnızca elemanlarının değişmeyeceği, const
'tan sonraki parantezin yalnızca elemanın türünü içermesi ile sağlanır. Yukarıdaki kod buna uygun olarak değiştirilirse artık yalnızca elemanı değiştiren satır derlenemez; dilimin kendisi değiştirilebilir:
const(int)[] dilim = [ 1, 2 ]; dilim ~= 3; // şimdi derlenir dilim[0] = 3; // ← derleme HATASI dilim.length = 1; // şimdi derlenir const int[] başkaDilim = [ 10, 11 ]; dilim = başkaDilim; // şimdi derlenir
Birbirlerine çok yakın olan bu söz dizimlerini şöyle karşılaştırabiliriz:
const int[] a = [1]; /* Ne elemanları ne kendisi değiştirilebilen dilim */ const(int[]) b = [1]; /* Üsttekiyle aynı anlam */ const(int)[] c = [1]; /* Elemanları değiştirilemeyen ama kendisi değiştirilebilen dilim */
Daha önceki bölümlerde bu konuyla üstü kapalı olarak karşılaştık. Hatırlarsanız, dizgi türlerinin asıl türlerinin immutable
olduklarından bahsetmiştik:
string
,immutable(char)[]
'ın takma ismidirwstring
,immutable(wchar)[]
'ın takma ismidirdstring
,immutable(dchar)[]
'ın takma ismidir
Benzer şekilde, dizgi hazır değerleri de değişmezdirler:
"merhaba"c
hazır dizgisinin türüstring
'dir"merhaba"w
hazır dizgisinin türüwstring
'dir"merhaba"d
hazır dizgisinin türüdstring
'dir
Bunlara bakarak D dizgilerinin normalde immutable
karakterlerden oluştuklarını söyleyebiliriz.
const
ve immutable
geçişlidir
Yukarıdaki a
ve b
dilimlerinin kod açıklamalarında da değinildiği gibi, o dilimlerin ne kendileri ne de elemanları değiştirilebilir.
Bu, ilerideki bölümlerde göreceğimiz yapılar ve sınıflar için de geçerlidir. Örneğin, const
olan bir yapı değişkeninin bütün üyeleri de const
'tır ve immutable
olan bir yapı değişkeninin bütün üyeleri de immutable
'dır. (Aynısı sınıflar için de geçerlidir.)
.dup
ve .idup
Karakterleri değişmez olduklarından dizgiler işlevlere parametre olarak geçirilirken uyumsuz durumlarla karşılaşılabilir. Bu durumlarda dizilerin .dup
ve .idup
nitelikleri yararlıdır:
.dup
dizinin değişebilen bir kopyasını oluşturur; ismi, "kopyasını al" anlamındaki "duplicate"ten gelir.idup
dizinin değişmez bir kopyasını oluşturur; ismi "immutable duplicate"ten gelir
Örneğin, parametresinin programın çalışması süresince kesinlikle değişmeyecek olmasını isteyen ve bu yüzden onu immutable
olarak belirlemiş olan bir işlevi .idup
ile alınan bir kopya ile çağırmak gerekebilir:
void foo(string dizgi) { // ... } void main() { char[] selam; foo(selam); // ← derleme HATASI foo(selam.idup); // ← derlenir }
Nasıl kullanmalı
- Genel bir kural olarak, olabildiği kadar değişmezliği yeğleyin.
- Değişken tanımlarken, programın çalışması sırasında kesinlikle değişmeyecek olan ve değerleri derleme zamanında bilinen veya hesaplanabilen değerleri
enum
olarak tanımlayın. Örneğin, dakikadaki saniye sayısı değişmez:enum int dakikaBaşınaSaniye = 60;
Türün sağ taraftan çıkarsanabildiği durumlarda değişkenin türü belirtilmeyebilir:
enum dakikaBaşınaSaniye = 60;
enum
dizisi veenum
eşleme tablosu kullanmanın gizli bedelini göz önünde bulundurun. Programda birden fazla yerde kullanıldıklarında onlarıimmutable
değişkenler olarak tanımlayın.- Kesinlikle değişmeyecek olan ama değerleri derleme zamanında bilinmeyen veya hesaplanamayan değişkenleri
const
olarak tanımlayın. Türün belirtilmesi yine isteğe bağlıdır:const tahmin = sayıOku("Tahmininiz");
- Parametre tanımlarken, eğer işlev parametrede bir değişiklik yapmayacaksa parametreyi
in
olarak tanımlayın. Öyle yaptığınızda parametreyi değiştirmeme sözü verdiğinizden, değişebilen,const
, veimmutable
değişkenleri o işleve gönderebilirsiniz:void foo(in char[] dizgi) { // ... } void main() { char[] değişebilenDizgi; string immutableDizgi; foo(değişebilenDizgi); // ← derlenir foo(immutableDizgi); // ← derlenir }
- Bu kitaptaki çoğu örneğin aksine, parametrelerin hangi bölümlerinin değişmez olduğunu belirtin:
// Elemanlar değişemez, dilim değişebilir void yazdır_1(const(int)[] dilim) { // ... } // Ne dilim ne de elemanlar değişebilir void yazdır_2(const(int[]) dilim) { // ... } // yazdır_2() ile aynı // (bu kitaptaki örneklerin aksine, bu yöntemden kaçının) void yazdır_3(const int[] dilim) { // ... }
const
değişkenleriimmutable
parametre alan işlevlere gönderemeyeceğinizi unutmayın. Bu konunun ayrıntılarını yukarıdaki "const
parametre mi,immutable
parametre mi?" başlığında gördük.- Eğer parametrede bir değişiklik yapacaksanız o parametreyi değişebilen şekilde tanımlayın (
in
,const
, veyaimmutable
olarak tanımlansa zaten derleyici izin vermez):import std.stdio; void tersÇevir(dchar[] dizgi) { foreach (i; 0 .. dizgi.length / 2) { immutable geçici = dizgi[i]; dizgi[i] = dizgi[$ - 1 - i]; dizgi[$ - 1 - i] = geçici; } } void main() { dchar[] selam = "merhaba"d.dup; tersÇevir(selam); writeln(selam); }
Çıktısı:
abahrem
Özet
enum
değişkenler, değerleri derleme zamanında bilinen ve kesinlikle değişmeyecek olan kavramları temsil ederler.enum
dizilerin veenum
eşleme tablolarının kodda geçtikleri her noktada bellek ayrılması gibi gizli bir bedelleri vardır. Diziler ve eşleme tabloları içinimmutable
belirtecini kullanın.const
parametreler yerinein
parametreleri yeğleyin.const
veimmutable
değişkenler, değerleri derleme zamanında bilinemeyen ama kesinlikle değişmeyecek olan kavramları temsil ederler.const
bir parametre, işlev tarafından değiştirilemez. O yüzden, hem değişebilen, hemconst
, hem deimmutable
değişkenler o parametrenin değeri olarak kullanılabilirler.immutable
parametreler, işlevin özellikleimmutable
olmasını talep ettiği parametrelerdir. İşlev çağrılırken bu parametrelere karşılık yalnızcaimmutable
değişkenler gönderilebilir.const(int[])
, dilimin de elemanlarının da değişmez olduklarını belirler.const(int)[]
, yalnızca elemanların değişmez olduklarını belirler.