it-swarm.asia

"Break" ve "devam" kötü programlama uygulamaları mıdır?

Patronum, kötü programcıların döngülerde break ve continue kullandığını dikkatsizce belirtiyor.

Onları her zaman kullanıyorum çünkü mantıklılar; size ilhamı göstereyim:

function verify(object) {
    if (object->value < 0) return false;
    if (object->value > object->max_value) return false;
    if (object->name == "") return false;
    ...
}

Buradaki nokta, önce fonksiyonun koşulların doğru olup olmadığını kontrol etmesinin ardından gerçek işlevselliği yürütmesidir. Aynı IMO döngüler için de geçerlidir:

while (primary_condition) {
    if (loop_count > 1000) break;
    if (time_exect > 3600) break;
    if (this->data == "undefined") continue;
    if (this->skip == true) continue;
    ...
}

Bence bu okumayı ve hata ayıklamayı kolaylaştırıyor; ama aynı zamanda bir dezavantaj da görmüyorum.

206
Mikhail

Bir bloğun başlangıcında kullanıldığında, ilk kontroller yapıldığı gibi, ön koşullar gibi davranırlar, bu yüzden iyidir.

Bloğun ortasında kullanıldığında, bazı kodlarla gizli tuzaklar gibi davranırlar, bu yüzden kötüdür.

253
Klaim

Donald Knuth'un 1974 tarihli makalesini İfadelere Git ile Yapısal Programlama okuyabilir, burada go to yapısal olarak arzu edilir. break ve continue ifadelerinin eşdeğerini içerir (go to orada daha sınırlı yapılara geliştirilmiştir). Patronunuz Knuth'u kötü bir programcı olarak nitelendirecek türde mi?

(Verilen örnekler beni ilgilendiriyor. Tipik olarak, break ve continue herhangi bir kod parçasından bir girişi ve bir çıkışı sevenler tarafından beğenilmez ve bu tür bir kişi birden çok return ifadeleri.)

90
David Thornley

Kötü olduklarına inanmıyorum. Kötü oldukları fikri, yapılandırılmış programlama günlerinden gelir. Bir işlevin tek bir giriş noktası ve tek bir çıkış noktası olması gerektiği ile ilgilidir, i. e. işlev başına yalnızca bir return.

Bu, işleviniz uzunsa ve birden çok iç içe döngünüz varsa bir anlam ifade eder. Bununla birlikte, işlevleriniz kısa olmalı ve döngüler ve bedenlerini kendi kısa işlevlerine sarmalısınız. Genel olarak, bir işlevi tek bir çıkış noktasına sahip olmaya zorlamak çok kıvrık mantığa neden olabilir.

İşleviniz çok kısaysa, tek bir döngünüz varsa veya en kötü iki iç içe döngünüz varsa ve döngü gövdesi çok kısaysa, bir break veya continue yapar. Birden fazla return ifadesinin ne yaptığı da açıktır.

Bu sorunlar Robert C. Martin tarafından "Temiz Kod" ve Martin Fowler tarafından "Yeniden Düzenleme" bölümünde ele alınmaktadır.

46
Dima

Kötü programcılar mutlak konuşurlar (tıpkı Sith gibi). İyi programcılar mümkün olan en açık çözümü kullanır (diğer tüm şeyler eşittir).

Break and continue komutunu sık sık kullanmak kodu izlemeyi zorlaştırır. Ancak bunları değiştirmek kodu takip etmeyi daha da zorlaştırıyorsa, bu kötü bir değişikliktir.

Verdiğiniz örnek kesinlikle molaların ve devamların daha zarif bir şeyle değiştirilmesi gerektiği bir durumdur.

44
Matthew Read

Çoğu insan bunun kötü bir fikir olduğunu düşünür, çünkü davranış kolayca tahmin edilemez. Kodu okuyorsanız ve while(x < 1000){} görüyorsanız, x> = 1000'e kadar çalışacağını varsayıyorsunuz ... Ama ortada aralar varsa, bu doğru olmaz, bu yüzden Döngünüze gerçekten güvenemezsiniz ...

İnsanların GOTO'yu sevmemesinin aynı nedeni: elbette, can iyi kullanılmalıdır, ancak kodun bölümden bölüme rastgele sıçradığı godawful spagetti koduna da yol açabilir.

Kendim için, birden fazla koşulda kırılan bir döngü yapacak olsaydım, while(x){} yapardım ve sonra ayrılmam gerektiğinde X'i false olarak değiştirirdim. Nihai sonuç aynı olur ve kodu okuyan herkes X'in değerini değiştiren şeylere daha yakından bakmayı bilir.

25
Satanicpuppy

Evet, kesme ifadeleri olmadan programları yeniden yazabilirsiniz (ya da aynı şeyi yapan döngülerin ortasından döner). Ancak, her ikisini de programın anlaşılmasını zorlaştıran ek değişkenler ve/veya kod çoğaltmaları tanıtmanız gerekebilir. Pascal (programlama dili) bu nedenle özellikle yeni başlayan programcılar için çok kötüydü. Patronunuz temelde Pascal'ın kontrol yapılarında program yapmanızı istiyor. Linus Torvalds ayakkabılarının içinde olsaydı, muhtemelen patronuna orta parmağını gösterecekti!

Kosaraju'nun kontrol yapıları hiyerarşisi olarak adlandırılan ve 1973'e dayanan ve 1972'den beri Knuth'un gotos üzerindeki ünlü makalesinde bahsedilen bir bilgisayar bilimi sonucu var. (Bu Knuth makalesi yukarıda David Thornley tarafından önerildi. .. S. Rao Kosaraju'nun 1973'te kanıtladığı şey, çok seviyeli derinlik kopması n olan programların tekrarlanan programlara yeniden yazılmasının mümkün olmamasıdır. fazladan değişkenler girmeden derinlik n değerinden daha azdır. Ama diyelim ki bu sadece teorik bir sonuç. (Sadece birkaç ekstra değişken ekleyin ?! Elbette bunu patronunuzu memnun etmek için yapabilirsiniz ...)

Yazılım mühendisliği açısından çok daha önemli olan, Eric S. Roberts'ın Döngü Çıkışları ve Yapısal Programlama: Tartışmayı Yeniden Açma ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE-1995/LoopExits.pdf ). Roberts, başkaları tarafından önündeki çeşitli ampirik çalışmaları özetler. Örneğin, bir grup CS101 tipi öğrenciden bir dizide sıralı arama yapan bir işlev için kod yazması istendiğinde, çalışmanın yazarı, programdan çıkmak için bir ara/geri dön/goto kullanan öğrenciler hakkında şunları söyledi: öğe bulunduğunda sıralı arama döngüsü:

Henüz [bu tarzı] kullanarak yanlış bir çözüm üreten bir programı deneyen tek bir kişi bulamadım.

Roberts ayrıca şunları söylüyor:

Sorunu for döngüsünden açık bir dönüş kullanmadan çözmeye çalışan öğrenciler çok daha az iyi sonuç verdi: bu stratejiyi deneyen 42 öğrenciden sadece yedisi doğru çözümler üretmeyi başardı. Bu rakam% 20'den daha düşük bir başarı oranını temsil eder.

Evet, CS101 öğrencilerinden daha deneyimli olabilirsiniz, ancak break ifadesini kullanmadan (veya döngülerin ortasından eşdeğer olarak geri dön/goto) kullanmadan, sonunda nominal olarak güzel yapılandırılmışken ekstra mantık açısından yeterince tüylü olan bir kod yazacaksınız. değişkenler ve kod çoğaltma, muhtemelen kendiniz, patronunuzun kodlama stilini takip etmeye çalışırken mantık hataları koyacaktır.

Burada da Roberts'ın makalesinin ortalama programcı için çok daha erişilebilir olduğunu söyleyeceğim, bu yüzden Knuth'unkinden daha iyi bir ilk okuma. Ayrıca daha kısadır ve daha dar bir konuyu kapsar. CS tipi yerine yönetim olsa bile muhtemelen patronunuza tavsiye edebilirsiniz.

16
Fizz

Bu kötü uygulamalardan birini kullanmayı düşünmüyorum, ancak bunları aynı döngü içinde çok fazla kullanmak döngüde kullanılan mantığı yeniden düşünmeyi gerektirecektir. Onları idareli kullanın.

9
Bernard

Verdiğiniz örneğin molalara veya devam etmesine gerek yok:

while (primary-condition AND
       loop-count <= 1000 AND
       time-exec <= 3600) {
   when (data != "undefined" AND
           NOT skip)
      do-something-useful;
   }

Örneğinizdeki 4 satırla ilgili 'sorunum', hepsinin aynı seviyede olmaları, ancak farklı şeyler yapmalarıdır: bazıları ara veriyor, bazıları devam ediyor ... Her satırı okumalısınız.

İç içe yaklaşımımda, ne kadar derine giderseniz, kod o kadar yararlı olur.

Ancak, derinlerde döngüyü durdurmak için bir neden bulursanız (birincil koşul dışında), bir mola veya geri dönüş kullanacaktır. Ben üst düzey durumda test edilecek ekstra bir bayrak kullanımı tercih ederim. Kesme/geri dönüş daha doğrudan; amacı başka bir değişken ayarlamaktan daha iyi belirtir.

7
Peter Frings

"Kötülük" onları nasıl kullandığınıza bağlıdır. Ben genellikle bir algoritmanın yeniden düzenlenmesi ile kaydedilemeyen döngüleri kurtaracak zaman döngü yapılarda molalar kullanın. Örneğin, belirli bir özellikteki değeri true olarak ayarlanmış bir öğe arayan bir koleksiyonda dolaşmak. Bilmeniz gereken tek şey, öğelerden birinin bu özelliğin true değerine ayarlanmış olması durumunda, bu sonuca ulaştığınızda, bir döngüyü uygun şekilde sonlandırmak iyi olur.

Bir mola kullanmak kodu özellikle okumayı kolaylaştıramazsa, işlemlerde döngüleri önemli ölçüde çalıştırır veya kaydeder, bunları kullanmamak en iyisidir. Beni takip eden herkes benim kod kolayca bakmak ve ne olduğunu anlamaya emin olmak için mümkün olduğunda "en düşük ortak payda" kod eğilimindedir (Ben her zaman bu konuda başarılı değilim). Aralar bunu azaltır çünkü tek giriş/çıkış noktaları oluştururlar. Kötüye kullanımı çok fazla "goto" ifadesi gibi davranabilir.

6
Joel Etherton

Kesinlikle hayır ... Evet goto kullanımı kötü çünkü programınızın yapısını bozar ve ayrıca kontrol akışını anlamak çok zordur.

Ancak bu günlerde break ve continue gibi ifadelerin kullanılması kesinlikle gerekli ve kötü programlama uygulaması olarak görülmüyor.

Ayrıca break ve continue kullanımındaki kontrol akışını anlamak o kadar da zor değil. switch gibi yapılarda break ifadesi kesinlikle gereklidir.

4
Radheshyam Nayak

İkinci kod snippet'inizi

while (primary_condition && (loop_count <= 1000 && time_exect <= 3600)) {
    if (this->data != "undefined" && this->skip != true) {
        ..
    }
}

herhangi bir terslik nedeni için değil - aslında bunun daha kolay okunması ve birisinin neler olup bittiğini anlaması gerektiğini düşünüyorum. Genel olarak, döngülerinizin koşulları yalnızca vücutta yer almayan döngü koşullarında bulunmalıdır. Ancak, break ve continue öğelerinin okunabilirliğe yardımcı olabileceği bazı durumlar vardır. break moreso continue ekleyebilirim: D

3
El Ronnoco

Temel düşünce, programınızı anlamsal olarak analiz edebilmenizdir. Tek bir girişiniz ve tek bir çıkışınız varsa, olası durumları belirtmek için gereken matematik, çatal yollarını yönetmeniz gerekenden çok daha kolaydır.

Kısmen, bu zorluk, kodunuz hakkında kavramsal olarak akıl yürütmeye dönüşüyor.

Açıkçası, ikinci kodunuz belli değil. Ne yapıyor? Devam 'devam ediyor mu', yoksa 'sonraki' döngü mü? Hiç bir fikrim yok. En azından ilk örneğiniz açık.

3
Paul Nathan

Patronuna katılıyorum. Kötüdürler çünkü yüksek siklomatik karmaşıklığa sahip yöntemler üretirler. Bu tür yöntemlerin okunması ve test edilmesi zordur. Neyse ki kolay bir çözüm var. Döngü gövdesini, "devam" ın "dönüş" olduğu ayrı bir yönteme ayıklayın. "Return" daha iyidir çünkü "return" dan sonra biter - yerel eyalet hakkında hiçbir endişe yoktur.

"Break" için döngü "break" yerine "break" yerine, ayrı bir yöntem içine döngü ayıklayın.

Ayıklanan yöntemler çok sayıda bağımsız değişken gerektiriyorsa, bu bir sınıfı ayıklamak için bir göstergedir - ya onları bir bağlam nesnesine toplayın.

2
kevin cline

Patronuna katılmıyorum. break ve continue için kullanılacak uygun yerler var. Aslında, modern programlama dillerinde yürütmeler ve istisna işlemenin getirilmesinin nedeni, her sorunu sadece structured techniques.

Bir yan not Burada dini bir tartışma başlatmak istemiyorum ama kodunuzu daha da okunabilir olacak şekilde yeniden yapılandırabilirsiniz:

while (primary_condition) {
    if (loop_count > 1000) || (time_exect > 3600) {
        break;
    } else if ( ( this->data != "undefined") && ( !this->skip ) ) {
       ... // where the real work of the loop happens
    }
}

Başka bir notta

Şahsen ( flag == true ) koşullu, çünkü değişken zaten bir boole ise, booleanın değerinin istediğiniz cevaba sahip olması durumunda olması gereken ek bir karşılaştırma yapıyorsunuz - tabii ki derleyicinizin bu ekstra karşılaştırmayı optimize edeceğinden emin değilseniz uzakta.

2
Zeke Hansell

Prensipte continue ve break'e karşı değilim, ama bence çok daha iyi bir şeyle değiştirilebilecek çok düşük seviyeli yapılar.

Burada bir örnek olarak C # kullanıyorum, bir koleksiyon üzerinde yineleme yapmak istediğinizi düşünün, ancak sadece bazı yüklemleri yerine getiren unsurları istiyoruz ve maksimum 100 iterasyondan fazlasını yapmak istemiyoruz.

for (var i = 0; i < collection.Count; i++)
{
    if (!Predicate(item)) continue;
    if (i >= 100) break; // at first I used a > here which is a bug. another good thing about the more declarative style!

    DoStuff(item);
}

Bu GERÇEKTEN temiz görünüyor. Anlamak çok zor değil. Yine de daha bildirimsel olmaktan çok şey kazanmanın iyi olacağını düşünüyorum. Aşağıdakilerle karşılaştırın:

foreach (var item in collection.Where(Predicate).Take(100))
    DoStuff(item);

Belki Where ve Take çağrıları bu yöntemde bile olmamalıdır. Belki bu filtreleme, toplama bu yönteme geçmeden ÖNCE yapılmalıdır. Her neyse, düşük seviyeli şeylerden uzaklaşarak ve gerçek iş mantığına daha fazla odaklanarak, GERÇEKTEN ilgilendiğimiz daha açık hale geliyor. Kodumuzu, iyi tasarım uygulamalarına daha fazla bağlı olan uyumlu modüllere ayırmak daha kolay hale geliyor. üzerinde.

Kodun bazı kısımlarında düşük seviyeli şeyler var olmaya devam edecek, ancak bunu olabildiğince gizlemek istiyoruz, çünkü bunun yerine iş sorunları hakkında akıl yürütmek için kullanabileceğimiz zihinsel enerji gerekiyor.

0
sara

Bence bu sadece çoklu döngülerin içine yerleştirildiğinde bir problem. Hangi döngüye girdiğinizi bilmek zor. Devam etmeyi takip etmek zor olabilir, ama bence gerçek acı molalardan geliyor - mantığı takip etmek zor olabilir.

0
davidhaskins

Bu tarzlardan hiçbirini sevmiyorum. İşte ne tercih ederim:

function verify(object)
{
    if not (object->value < 0) 
       and not(object->value > object->max_value)
       and not(object->name == "") 
       {
         do somethign important
       }
    else return false; //probably not necessary since this function doesn't even seem to be defined to return anything...?
}

Gerçekten bir işlevi iptal etmek için return kullanmayı sevmiyorum. return kötüye kullanımı gibi geliyor.

break kullanımı da her zaman net değildir.

Daha da iyisi olabilir:

notdone := primarycondition    
while (notDone)
{
    if (loop_count > 1000) or (time_exect > 3600)
    {
       notDone := false; 
    }
    else
    { 
        skipCurrentIteration := (this->data == "undefined") or (this->skip == true) 

        if not skipCurrentIteration
        {
           do something
        } 
        ...
    }
}

daha az iç içe yerleştirme ve karmaşık koşullar değişkenlere dönüştürülür (gerçek bir programda daha iyi isimlere sahip olmanız gerekir, tabii ki ...)

(Yukarıdaki kod yalancı koddur)

Hayır. Bir sorunu çözmenin bir yolu ve sorunu çözmenin başka yolları da var.

Birçok geçerli ana dil (Java, .NET (C # + VB), PHP, kendi yazınızı yazın) döngüleri atlamak için "break" ve "devam" komutlarını kullanır. İkisi de "yapılandırılmış goto (lar)" cümleleri.

Onlarsız:

String myKey = "mars";

int i = 0; bool found = false;
while ((i < MyList.Count) && (not found)) {
  found = (MyList[i].key == myKey);
  i++;   
}
if (found)
  ShowMessage("Key is " + i.toString());
else
  ShowMessage("Not found.");

Onlarla:

String myKey = "mars";

for (i = 0; i < MyList.Count; i++) {
  if (MyList[i].key == myKey)
    break;
}
ShowMessage("Key is " + i.toString());

"Break" ve "devam" kodlarının daha kısa olduğunu ve genellikle "cümle" veya "foreach" cümlelerini "cümle" haline getirdiğini unutmayın.

Her iki durum da bir kodlama tarzıdır. Onları kullanmamayı tercih ediyorum, çünkü ayrıntılı stil kod üzerinde daha fazla kontrol sahibi olmamı sağlıyor.

Aslında, bu cümleleri kullanmanın zorunlu olduğu bazı projelerde çalışıyorum.

Bazı geliştiriciler, gerekli olmadığını düşünebilirler, ancak varsayımsaldır, eğer onları kaldırmak zorunda kalırsak, "while" ve "while while" ("yinelemek" e kadar, siz Pascal çocuklar) ;-)

Sonuç, onları kullanmamayı tercih etsem bile, Bence bu bir seçenek, kötü bir programlama uygulaması değil.

0
umlcat

Aşağıdaki örnekte olduğu gibi gizlenmiş goto olarak kullanılmadıkları sürece:

do
{
      if (foo)
      {
             /*** code ***/
             break;
      }

      if (bar)
      {
             /*** code ***/
             break;
      }
} while (0);

Onlarla iyiyim. (Üretim kodunda görülen örnek, meh)

0
SuperBloup