Neden mola anahtarını kullanmak zorundayız?


74

Her ifadede switchinşaatın (birçok dilde) kullanılması gerektiğine kim karar verdi (ve hangi kavramlara dayanarak) break?

Neden böyle bir şey yazmak zorundayız:

switch(a)
{
    case 1:
        result = 'one';
        break;
    case 2:
        result = 'two';
        break;
    default:
        result = 'not determined';
        break;
}

(PHP ve JS'de bunu fark ettim; muhtemelen bunu kullanan diğer birçok dil var)

Eğer switchbir alternatifse if, neden aynı yapıyı kullanamıyoruz if? yani:

switch(a)
{
    case 1:
    {
        result = 'one';
    }
    case 2:
    {
        result = 'two';
    }
    default:
    {
        result = 'not determined';
    }
}

breakMevcut olanı takip eden bloğun yürütülmesini önlediği söylenir . Ancak, mevcut bloğun uygulanmasına ve sonrakilerin yürütülmesine ihtiyaç duyulan herhangi biri gerçekten durumun içine giriyor mu? Yapmadım Benim breakiçin her zaman orada. Her blokta. Her kodda.


1
Cevapların çoğunun belirttiği gibi, bu, birden fazla koşulun aynı “o zaman” bloklarından geçirildiği “düşme” ile ilgilidir. Bununla birlikte, bu dile bağlıdır - RPG, örneğin, bir CASEifadeyi dev bir if / elseif bloğu ile eşit olarak ele alır .
Clockwork-Muse

34
Bu C-tasarımcıları tarafından yapılan kötü karardı - (doğrusu programlama kolaylığı için daha> C ve geri çeviri, daha kolay, birçok kararlar gibi, montaj geçiş yapmak için yapıldı) sonra maalesef miras, Diğer C tabanlı dillerde. Basit kuralı kullanarak "Her zaman ortak vakayı varsayılan yap !!" , varsayılan davranışın , en yaygın durum olduğundan bu yana, düşmek için ayrı bir anahtar kelimeyle kırılması gerektiğini görebiliriz .
BlueRaja - Danny Pflughoeft

4
Her ne kadar bazı durumlarda düşüşe geçtim, ancak tartışmasız tercih edilen dilimdeki (C) anahtar deyiminden kaçınmak için tasarlanmış olabilir; örneğin yaptım case 'a': case 'A': case 'b': case 'B'ama çoğunlukla yapamam çünkü case in [ 'a', 'A', 'b', 'B' ]. Biraz daha iyi bir soru, benim şimdiki tercih edilen dil (C #) 'de, kırılma mesafesindedir zorunlu ve orada hayır örtülü sonbahar-gerçi; unutmak breaksözdizimi hatası ...: \
KutuluMike

1
Gerçek kodla yapılan düşüş genellikle çözümleyici uygulamalarında bulunur. case TOKEN_A: /*set flag*/; case TOKEN_B: /*consume token*/; break; case TOKEN_C: /*...*/
OrangeDog

1
@ BlueRaja-DannyPflughoeft: C aynı zamanda derlenmesi kolay olacak şekilde tasarlanmıştır. “Eğer bir sıçrama Emit breakdaha uygulamak için bir ölçüde daha basit kuraldır yerde mevcut” “eğer bir sıçrama yayarlar etmeyin fallthroughbir mevcuttur switch”.
Jon Purdy

Yanıtlar:


95

C switchifadesi bu formda ilk dillerden biriydi ve diğer tüm ana diller onu oradan devraldı, çoğunlukla C semantiğini varsayılan olarak tutmayı seçtiler - ya değiştirmenin avantajlarını düşünmediler ya da değerlendirdiler. Onları herkesin alıştığı davranışı sürdürmekten daha az önemliydi.

C'nin neden bu şekilde tasarlandığına gelince, muhtemelen “taşınabilir montaj” olarak C kavramından kaynaklanıyor. Bu switchifade temel olarak bir dal tablosunun bir soyutlamasıdır ve bir dal tablosunun da örtük bir çöküşü vardır ve önlemek için ek bir atlama talimatı gerektirir.

Temel olarak, C tasarımcıları , assembler semantiğini varsayılan başına tutmayı da seçtiler.


3
VB.NET, onu değiştiren bir dilin örneğidir. Dava maddesine akması tehlikesi yoktur.
dürtmek

1
Tcl, bir sonraki maddeye geçmeyen başka bir dil. Neredeyse hiçbir zaman bunu da yapmak istemedim (belirli program türlerini yazarken C'nin aksine).
Donal Fellows 19 Ağustos'ta

4
C'nin ata dilleri, B ve daha eski BCPL , her ikisinde de (?) Anahtar ifadeleri var; BCPL onu heceledi switchon.
Keith Thompson

Ruby'nin dava açıklamaları geçmiyor; tek bir testte iki test değeri alarak benzer davranışlar elde edebilirsiniz when.
Nathan Long

86

Çünkü bu dillerdeki ifadelerin switchalternatifi değildir if ... else.

Kullanarak switch, bir kerede birden fazla koşulu eşleştirebiliriz, bu bazı durumlarda oldukça beğenilmektedir.

Örnek:

public Season SeasonFromMonth(Month month)
{
    Season season;
    switch (month)
    {
        case Month.December:
        case Month.January:
        case Month.February:
            season = Season.Winter;
            break;

        case Month.March:
        case Month.April:
        case Month.May:
            season = Season.Spring;
            break;

        case Month.June:
        case Month.July:
        case Month.August:
            season = Season.Summer;
            break;

        default:
            season = Season.Autumn;
            break;
    }

    return season;
}

15
more than one block ... unwanted in most casesSeninle aynı fikirde değilim.
Satish Pandey,

2
@DanielB Yine de dile bağlı; C # anahtarı ifadeleri bu olasılığa izin vermiyor.
Andy,

5
@Andy Hala düşüşten mi bahsediyoruz? C # switch ifadeleri kesinlikle buna izin verir (3. örneğe bakın), bu nedenle ihtiyaç duyulur break. Fakat evet, bildiğim kadarıyla Duff'un Cihazı C # 'da çalışmıyor.
Daniel B,

8
@DanielB Evet öyleyiz, ancak derleyici bir koşulu, kodu ve daha sonra ara vermeden başka bir koşula izin vermez. Aralarında kod olmadan istiflenmiş çoklu koşullara sahip olabilirsiniz veya bir ara vermeniz gerekebilir. Bağlantınızdan C# does not support an implicit fall through from one case label to another. Düşebilirsin, ama bir ara vermeyi kazara unutma şansı yok.
Andy,

3
@Matsemann .. Artık tüm şeyleri kullanarak yapabiliriz, if..elseancak bazı durumlarda switch..casetercih edilebilir. Yukarıdaki örnek eğer if-else kullanırsak biraz karmaşık olur.
Satish Pandey

15

Bu C bağlamında Yığın Taşması sorulmuştur: switch ifadesi neden bir mola vermek üzere tasarlanmıştır?

Kabul edilen cevabı özetlemek gerekirse, muhtemelen bir hataydı. Diğer pek çok dil muhtemelen C'yi yeni izlemiştir. Bununla birlikte, C # gibi bazı diller bunu başarabilmeye izin vererek düzelttiler - ancak yalnızca programcı açıkça söylediğinde (kaynak: yukarıdaki bağlantı, C # kendim konuşamıyorum) .


Google Go, IIRC’de aynı şeyi yapar (açıkça belirtilmişse açılışta izin verir).
Leo,

PHP de öyle. Ve sonuçta ortaya çıkan koda daha fazla bakarsanız, ne kadar rahatsız edici hissettirirse. Bunu /* FALLTHRU */söylemek istediğimi kanıtlamak için yukarıdaki @Tapio ile bağlanan yanıttaki yorumu beğendim .
DaveP

1
C # 'a goto case, bağlantının yaptığı gibi, bunun neden bu kadar iyi çalıştığını göstermez. Yukarıdaki gibi birden fazla durumda yığın oluşturabilirsiniz, ancak kodu goto case whatevergirer girmez, bir sonraki davaya girmeye açık olmanız veya bir derleyici eroru edinmeniz gerekir. Bana ikisinden sorarsanız, istemeden ihmalden kaçınılması konusunda endişelenmeden davaları doldurma kabiliyeti, açık bir şekilde düşüşe izin vermekten çok daha iyi bir özelliktir goto case.
Jesper

9

Bir örnekle cevap vereceğim. Bir yılın her ayı için gün sayısını listelemek istiyorsanız, bazı ayların 31, bazı 30 ve 1 28/29 olduğu açıktır. Bu gibi görünecek

switch(month) {
    case 4:
    case 6:
    case 9:
    case 11;
        days = 30;
        break;
    case 2:
        //Find out if is leap year( divisible by 4 and all that other stuff)
        days = 28 or 29;
        break;
    default:
        days = 31;
}

Bu, birden fazla vakanın aynı etkiye sahip olduğu ve hepsinin birlikte gruplandığı bir örnektir. Break anahtar sözcüğünün seçilmesinin bir nedeni vardı ve if ... else eğer yapılmadı .

Burada dikkat edilmesi gereken en önemli şey , pek çok benzer vakaya sahip bir anahtar ifadesinin bir if değil ... başka bir durumda olması durumunda ... başka bir durumda ... başka her durumda başka bir durumda olması değil , (1, 2, 3). ... başka eğer (4,5,6) başka ...


2
Örneğiniz, iffaydası olmayan bir şeyden daha ayrıntılı görünüyor . Belki de örneğinize bir isim verilmişse veya düşüşe ek olarak aylara özgü çalışmalar yapmış olsaydı, düşüşün faydası daha belirgin olurdu? (ilk etapta bir kişi olmalı mı diye yorum yapmadan)
horatio

1
Bu doğru. Bununla birlikte, sadece düşme durumunun kullanılabileceği vakaları göstermeye çalışıyordum. Ayların her birinin ayrı ayrı bir şeyler yapması gerekiyorsa çok daha iyi olacağı açıktır.
Awemo

8

Bir davadan diğerine “geçmenin” iki durumu vardır - boş dava:

switch ( ... )
{
  case 1:
  case 2:
    do_something_for_1_and_2();
    break;
  ...
}

ve boş olmayan dava

switch ( ... )
{
  case 1:
    do_something_for_1();
    /* Deliberately fall through */
  case 2:
    do_something_for_1_and_2();
    break;
...
}

Duff'un Cihazına yapılan atıfta bulunmaksızın, ikinci durumun meşru örnekleri az ve çoktur ve genellikle kodlama standartları tarafından yasaklanır ve statik analiz sırasında işaretlenir. Ve bulunduğu yerde, a 'nın ihmal edilmemesinden dolayı daha sık görülür break.

İlki mükemmel derecede mantıklı ve ortaktır.

Dürüst olmak gerekirse, breakboş bir dava bağımsız olduğunda, boş bir dava gövdesinin çöktüğünü bilen ve dil ayrıştırıcıya ihtiyaç duymak için hiçbir neden göremiyorum .

ISO C panelinin, tanımsız, tanımlanmamış veya uygulama tarafından tanımlanmış özellikleri tespit etmekten ziyade, mantıksız olandan söz etmemek yerine dile yeni (istenmeyen) ve kötü tanımlanmış özellikler eklemekle meşgul olması üzücü.


2
Şükürler olsun ki, ISO C dilde kırılma değişiklikleri getirmiyor!
Jack Aidley

4

Zorla breakyaptırmamak, başka türlü yapılması zor olabilecek bazı şeylere izin verir. Diğerleri, bazı önemsiz vakaların bulunduğu gruplama vakalarını belirtmiştir.

breakKullanılmaması zorunlu olan bir durum Duff Cihazı'dır . Bu "için kullanılır Penis gerekli karşılaştırmalar sayısını sınırlayarak işlemlerini hızlandırabilir burada" döngüler. İlk kullanımın, daha önce tamamen yuvarlanmış bir döngü ile çok yavaş olan işlevlere izin verdiğine inanıyorum. Bazı durumlarda hız için kod boyutunda işlem yapar.

breakDavanın herhangi bir kodu varsa, uygun bir yorum ile değiştirmek iyi bir uygulamadır . Aksi takdirde, birileri eksik olanı düzeltecek breakve bir hata tanıtacaktır.


Duff's Device örneği için (+1) teşekkürler . Farkında değildim.
trejder

3

Kökeni göründüğü C, switchifadenin kod bloğu özel bir yapı değil. Normal bir kod bloğudur, tıpkı bir ififadenin altındaki bir blok gibi .

switch ()
{
}

if ()
{
}

caseve defaultbu blok içindeki, özellikle bunlarla ilgili olan atlama etiketleridir switch. Onlar sadece normal atlama etiketleri için kullanılır goto. Burada önemli olan belirli bir kural vardır: Jump etiketleri kod akışını kesintiye uğratmadan hemen hemen her yerde olabilir.

Normal bir kod bloğu olarak, bileşik bir ifade olması gerekmez. Etiketler de isteğe bağlıdır. Bunlar switchC'deki geçerli ifadelerdir:

switch (a)
    case 1: Foo();

switch (a)
    Foo();

switch (a)
{
    Foo();
}

C standardının kendisi bunu bir örnek olarak verir (6.8.4.2):

switch (expr) 
{ 
    int i = 4; 
    f(i); 
  case 0: 
    i=17; 
    /*falls through into default code */ 
  default: 
    printf("%d\n", i); 
} 

Yapay program fragmanında, kimliği olan nesne otomatik saklama süresi ile (blok içinde) var olan ancak hiçbir zaman başlatılmayan nesnedir ve bu nedenle kontrol ifadesinin sıfır olmayan bir değeri varsa, printf işlevine yapılan çağrı belirsiz bir değere erişir. Benzer şekilde, f işlevine yapılan çağrıya erişilemiyor.

Ayrıca, defaultaynı zamanda bir atlama etiketidir ve bu nedenle son durum olmaya gerek kalmadan her yerde olabilir.

Bu ayrıca Duff'un Aygıtını da açıklar:

switch (count % 8) {
    case 0: do {  *to = *from++;
    case 7:       *to = *from++;
    case 6:       *to = *from++;
    case 5:       *to = *from++;
    case 4:       *to = *from++;
    case 3:       *to = *from++;
    case 2:       *to = *from++;
    case 1:       *to = *from++;
            } while(--n > 0);
}

Neden düşüş? Normal bir kod bloğundaki normal kod akışında , bir kod bloğunda beklediğiniz gibi bir sonraki ifadeye geçilmesi beklenirif .

if (a == b)
{
    Foo();
    /* "Fall-through" to Bar expected here. */
    Bar();
}

switch (a)
{
    case 1: 
        Foo();
        /* Automatic break would violate expected code execution semantics. */
    case 2:
        Bar();
}

Tahminime göre bunun sebebi uygulama kolaylığı. Bir switchbloğu ayrıştırmak ve derlemek , özel kurallara bakmak için özel bir koda ihtiyacınız yoktur . Sadece diğer kodlar gibi ayrıştırırsınız ve sadece etiketlere ve atlama seçimine dikkat etmelisiniz.

Tüm bunlardan ilginç bir takip sorusu, aşağıdaki iç içe geçmiş ifadelerin "Tamamlandı" yazmasıdır. ya da değil.

int a = 10;

switch (a)
{
    switch (a)
    {
        case 10: printf("Done.\n");
    }
}

C standardı bunu önemser (6.8.4.2.4):

Bir vaka veya varsayılan etikete yalnızca en yakın çevre kapatma anahtarı ifadesinde erişilebilir.


1

Birkaç kişi, zaman zaman çok değerli olan birden fazla koşulun eşleştirilmesi fikrinden çoktan bahsetti. Bununla birlikte, birden fazla koşulu eşleştirme yeteneği, mutlaka eşleşen her koşulla aynı şeyi yapmanızı gerektirmez. Aşağıdakileri göz önünde bulundur:

switch (someCase)
{
    case 1:
    case 2:
        doSomething1And2();
        break;

    case 3:
        doSomething3();

    case 4:
        doSomething3And4();
        break;

    default:
        throw new Error("Invalid Case");
}

Burada birden fazla koşul kümesinin eşleştirilmesinin iki farklı yolu vardır. Koşullar 1 ve 2 ile aynı kod dizisine girip aynı şeyi yaparlar. Bununla birlikte, 3. ve 4. koşullar altında, ikisi de arayarak son bulsa da doSomething3And4(), sadece 3 çağrı ile doSomething3().


Partiye geç kaldı, ancak işlev adından da anlaşılacağı gibi bu kesinlikle "1 ve 2" değil: Tetiklemede kodun hepsine yol açabilecek üç farklı durum var case 2, yani doğru olmak istiyorsak (ve yaparsak) işlev adı doSomethingBecause_1_OR_2_OR_1AND2()ya da bir şey olmalı (hala iyi yazılmış bir kod olduğu halde değişmez bir kodun iki farklı doSomethingBecause1or2()
olaya eşlik

Başka bir deyişle doSomething[ThatShouldBeDoneInTheCaseOf]1And[InTheCaseOf]2(),. Mantıksal ve / veya ile ilgili değil.
Panzercrisis

Öyle olmadığını biliyorum, ama neden insanların bu tür bir kod karşısında kafaları karışmış olsa da, bunun "ve" ne olduğunu kesin olarak açıklamalıdır, çünkü birisinin sadece işe yarayan bir cevapta "doğal ve" vs. tam olarak açıklandığında, cevabın kendisinde daha fazla açıklama yapılması ya da bu belirsizliği gidermek için işlev adının yeniden yazılması gerekir =)
Mike 'Pomax' Kamermans

1

Sorularınızın ikisine cevap vermek için.

C neden ara vermeye ihtiyaç duyuyor?

Cs köklerine "taşınabilir bir montajcı" olarak geliyor. Bunun gibi psudo kodunun yaygın olduğu yerler: -

    targets=(addr1,addr2,addr3);
    opt = 1  ## or 0 or 2
switch:
    br targets[opt]  ## go to addr2 
addr1:
    do 1stuff
    br switchend
addr2:
    do 2stuff
    br switchend
addr3
    do 3stuff
switchend:
    ......

switch ifadesi, daha yüksek düzeyde benzer işlevsellik sağlamak üzere tasarlanmıştır.

Hiç ara vermeden şalter var mı?

Evet bu oldukça yaygındır ve birkaç kullanım durumu vardır;

Öncelikle birkaç durumda aynı işlemi yapmak isteyebilirsiniz. Bunu, davaları üst üste istifleyerek yaparız:

case 6:
case 9:
    // six or nine code

Durum makinelerinde yaygın olan bir diğer kullanım durumu, bir durumun işlenmesinden sonra hemen başka bir duruma girmek ve işlemek istediğimizdir:

case 9:
    crashed()
    newstate=10;
case 10:
    claim_damage();
    break;

-2

Break cümlesi, kullanıcının en yakın çevreleyen anahtardan (sizin durumunuz için) çıkarken, yaparken veya aldığınız her şeyden çıkmasına izin veren bir atlama ifadesidir. bu kadar kolay.


-3

Yukarıda yazılanların hepsinde - bu konunun ana sonucu, eğer yeni bir dil tasarlayacaksanız - varsayılan olarak bir breakifade eklemek zorunda kalmamalısınız ve derleyici, sanki yaptığınız gibi davranacaktır.

Bir sonraki davaya devam etmek istediğiniz o nadir durumda istiyorsanız - sadece bir continueifade ile belirtin.

Bu, yalnızca kasanın içinde küme parantezleri kullanıyorsanız daha sonra da devam etmeyecek şekilde geliştirilebilir, böylece aynı aygıta aynı aygıta sahip birkaç ayın üstündeki örneklerle - her zaman ihtiyaç duyulmadan beklendiği gibi çalışır continue.


2
Devam ifadesine göre önerinizi anladığımdan emin değilim. Devam, C ve C ++ 'da çok farklı bir anlama sahip ve yeni bir dil C gibi olsaydı, ancak anahtar kelimeyi bazen C ile olduğu gibi kullandıysa ve bazen bu alternatif şekilde, karışıklığın hüküm süreceğini düşünüyorum. Bir etiketli bloktan diğerine düşme ile ilgili bazı sorunlar olsa da, paylaşılan bir kod bloğu ile aynı işleme sahip birden fazla durumun kullanımını seviyorum.
DeveloperDon

C, aynı anahtar kelimeleri birden fazla amaç için kullanma geleneğine sahiptir. Düşünün *, &, staticvb
idrougge
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.