switch
ve case
switch
, çoklu koşul gibi çalışan bir deyimdir ve bu açıdan bir "if else if" zincirine benzer. Buradaki kullanımında "durum" anlamına gelen case
, switch
'in denetlediği değerin karşılaştırıldığı durumları belirlemek için kullanılır; kendisi bir deyim değildir.
switch
, parantez içinde bir ifade alır; o ifadenin değerini kendi kapsamı içindeki case
'lerle karşılaştırır ve o değere eşit olan case
'in işlemlerini işletir. Söz dizimini şöyle gösterebiliriz:
switch (ifade) { case değer_1: // ifade'nin değer_1'e eşit olduğu durumdaki işlemler // ... break; case değer_2: // ifade'nin değer_2'ye eşit olduğu durumdaki işlemler // ... break; // ... başka case'ler ... default: // hiçbir değere uymayan durumdaki işlemler // ... break; }
Her ne kadar bir koşul gibi çalışsa da, switch
'in aldığı ifade bir mantıksal ifade olarak kullanılmaz. Yani bir if
'te olduğu gibi "eğer böyleyse" anlamında değildir. switch
'teki ifadenin değerinin, case
'lerdeki değerlere eşit olup olmadığına bakılır. Yani, buradaki koşullar hep eşitlik karşılaştırmalarıdır. Bu açıdan bakıldığında bir "if else if" zinciri gibi düşünülebilir:
auto değer = ifade; if (değer == değer_1) { // değer_1 durumundaki işlemler // ... } else if (değer == değer_2) { // değer_2 durumundaki işlemler // ... } // ... başka 'else if'ler ... } else { // hiçbir değere uymayan durumdaki işlemler // ... }
Ancak, bu "if else if" switch
'in tam eşdeğeri değildir. Nedenlerini aşağıdaki başlıklarda açıklıyorum.
İfadenin değerine eşit olan bir case
değeri varsa, o case
'in altındaki işlemler işletilir. Eğer yoksa, "varsayılan" anlamına gelen default
'un altındaki işlemler işletilir.
goto
goto
programcılıkta kaçınılması öğütlenen bir deyimdir. Buna rağmen nadir durumlarda switch
deyimi ile kullanılması gerekebilir. goto
deyimini ayrıntılı olarak daha ilerideki bir bölümde göreceğiz.
if
koşulunun kapsamı olduğu için, kapsamdaki işlemler sonlanınca bütün if
deyiminin işi bitmiş olur. switch
'te ise ifadenin değerine eşit bir case
bulunduğu zaman programın işleyişi o case
'e atlar ve ya bir break
ile ya da bir goto case
ile karşılaşılana kadar devam eder. goto case
hemen alttaki case
'e devam edilmesine neden olur:
switch (değer) { case 5: writeln("beş"); goto case; // bir sonraki case'e devam eder case 4: writeln("dört"); break; default: writeln("bilmiyorum"); break; }
değer
5 olduğunda case 5
satırının altına gidilir ve orada "beş" yazdırılır. Onun sonundaki goto case
bir sonraki case
'e devam edilmesini sağladığı için "dört" de yazdırılır ve çıktıda ikisi de yer alırlar:
beş dört
goto
deyimi case
bölümlerinde üç farklı biçimde kullanılabilir:
goto case
, bir sonrakicase
'e atlanmasını sağlar.goto default
,default
bölümüne atlanmasını sağlar.goto case ifade
, ifadeye uyancase
'e atlanmasını sağlar.
Bu üç kullanımı bir önceki bölümde gördüğümüz foreach
'ten de yararlanan aşağıdaki programla deneyebiliriz:
import std.stdio; void main() { foreach (değer; [ 1, 2, 3, 10, 20 ]) { writefln("--- değer: %s ---", değer); switch (değer) { case 1: writeln("case 1"); goto case; case 2: writeln("case 2"); goto case 10; case 3: writeln("case 3"); goto default; case 10: writeln("case 10"); break; default: writeln("default"); break; } } }
Çıktısı:
--- değer: 1 --- case 1 case 2 case 10 --- değer: 2 --- case 2 case 10 --- değer: 3 --- case 3 default --- değer: 10 --- case 10 --- değer: 20 --- default
İfadenin değeri ancak tamsayı, bool
, veya dizgi olabilir
if
'te eşitlik karşılaştırmasında herhangi bir tür kullanılabilir. switch
'te ise ifade değeri olarak ancak tamsayılar, bool, veya dizgiler kullanılabilir.
string işlem = /* ... */; // ... switch (işlem) { case "toplama": sonuç = birinci + ikinci; break; case "çıkarma": sonuç = birinci - ikinci; break; case "çarpma": sonuç = birinci * ikinci; break; case "bölme": sonuç = birinci / ikinci; break; default: throw new Exception(format("Geçersiz işlem: %s", işlem)); }
Not: Yukarıdaki kod hiçbir case
'e uymayan durumda bir hata atmaktadır. Hataları ilerideki bir bölümde göreceğiz.
Her ne kadar ifade türü olarak bool
da kullanılabiliyor olsa da, false
ve true
diye iki değeri olan bu tür için çoğu durumda if
'in veya ?:
üçlü işlecinin daha uygun olduğunu düşünebilirsiniz.
Değer aralıkları
Belirli bir değer aralığındaki durumlar case
'ler arasına ..
karakterleri yerleştirilerek belirtilir:
switch (zarDeğeri) { case 1: writeln("Sen kazandın"); break; case 2: .. case 5: writeln("Berabere"); break; case 6: writeln("Ben kazandım"); break; default: /* Aslında bu durumun hiç gerçekleşmemesi gerekir çünkü * yukarıdaki durumlar bütün olası değerleri * kapsamaktadır. (Aşağıdaki 'final switch'e bakınız.) */ break; }
Yukarıdaki zarla oynanan oyunda zarın 2, 3, 4, veya 5 değerinde berabere kalınmaktadır.
Ayrık değerler
Yukarıdaki oyunda [2,5] aralığında değil de 2 ve 4 değerleri geldiğinde berabere kalındığını varsayalım. Öyle durumlarda case
'in değerlerinin aralarına virgül yazılır:
case 2, 4: writeln("Berabere"); break;
final switch
deyimi
Bu deyim de switch
gibidir ama bazı kısıtlamaları vardır:
default
bölümü bulunamaz; zaten bu durum bazı koşullarda anlamsızdır: Örneğin, zarın değerlerinin altısının da işlemlerinin belirli olduğu bir durumdadefault
bölümüne gerek yoktur.case
'lerde aralıklı değerler kullanılamaz (virgülle gösterilen ayrık değerler ise kullanılabilir).- Eğer ifade bir
enum
türüyse türün bütün değerlerinincase
'ler tarafından kapsanmış olmaları gerekir (enum
'ları bir sonraki bölümde göreceğiz).
final switch (zarDeğeri) { case 1: writeln("Sen kazandın"); break; case 2, 3, 4, 5: writeln("Berabere"); break; case 6: writeln("Ben kazandım"); break; }
Ne zaman kullanmalı
Yukarıda anlatılanlardan anlaşıldığı gibi; switch
, bir ifadenin derleme zamanında bilinen değerlerle karşılaştırıldığı durumlarda kullanışlıdır.
Eğer karşılaştırılacak değer yalnızca iki taneyse, switch
yerine bir "if else" daha uygun olabilir. Örneğin yazı/tura gibi bir sonuçta if
deyimi yeterlidir:
if (yazıTuraSonucu == yazı) { // ... } else { // ... }
Genel bir kural olarak, switch
'i üç veya daha fazla değer olduğunda düşünebilirsiniz.
Mevcut değerlerin her birisinin case
değeri olarak yer alması gereken durumlarda final switch
'i yeğleyin. Bu, özellikle enum
türlerine uygundur.
Problemler
- Yukarıdaki örneklerden birisindeki gibi bir hesap makinesi yapın. Kullanıcıdan önce işlemi
string
olarak, sonra da sayılarıdouble
olarak alsın ve işleme göre hesap yapsın. Örneğin işlem "topla" olarak ve sayılar "5 7" olarak girildiğinde ekrana 12 yazsın.Girişi şu şekilde okuyabilirsiniz:
string işlem; double birinci; double ikinci; // ... işlem = strip(readln()); readf(" %s %s", &birinci, &ikinci);
- Hesap makinesini geliştirin ve "topla" gibi sözlü işlemler yanında "+" gibi simgeleri de desteklemesini sağlayın: işlem dizgisi olarak "+" girildiğinde de aynı şekilde çalışsın.
- Program bilinmeyen bir işlem girildiğinde hata atsın. Hata atma düzeneğini ilerideki bir bölümde göreceğiz. Şimdilik yukarıdaki
throw
deyimini kendi programınıza uygulayın.