Bu ifadelere gitmeyi haklı çıkarıyor mu?


15

Bu soruya bir saniye önce rastladım ve malzemenin bir kısmını oradan çıkarıyorum: 'break n' yapısı için bir isim var mı?

Bu, insanların programa çift iç içe bir döngüden çıkma talimatı vermeleri için gereksiz derecede karmaşık bir yol gibi görünüyor:

for (i = 0; i < 10; i++) {
    bool broken = false;
    for (j = 10; j > 0; j--) {
        if (j == i) {
            broken = true;
            break;
        }
    }
    if (broken)
        break;
}

Ders kitaplarının goto ifadelerinin şeytan olduğunu söylemeyi sevdiğini biliyorum ve bunlara hiç düşkün değilim, ama bu kuralın bir istisnasını haklı çıkarıyor mu?

Döngüler için n-nested ile ilgili cevaplar arıyorum.

NOT: İster tamamen dar görüşlü cevaplar, aradaki evet, hayır veya bir yere cevap yok edilir. Özellikle cevap hayır ise, o zaman iyi ve meşru bir neden belirtin (bu zaten Stack Exchange düzenlemelerinden çok uzak değildir).


2
İç döngü koşulunu şu şekilde yazabiliyorsanız for (j = 10; j > 0 && !broken; j--), break;ifadeye ihtiyacınız olmaz . Eğer brokendış döngü başlamadan önce deklarasyonunu hareket ettirirseniz , o zaman bu şekilde yazılabilirse, hiçbir s gerekli olmadığınıfor (i = 0; i < 10 && !broken; i++) düşünüyorum . break;
FrustratedWithFormsDesigner

8
Örnek C # 'dan alındığında - C # referansı goto: goto (C # Referans) - "goto ifadesi derin iç içe döngülerden kurtulmak için de yararlıdır."

Aman Tanrım, c # 'nın bir ifade ifadesi olduğunu hiç bilmiyordum! StackExchange'te öğrendikleriniz. (En son bir goto deyimi yeteneği istediğimi hatırlamıyorum, hiç ortaya çıkmamış gibi görünüyor).
John Wu

6
IMHO gerçekten sizin "programlama dini" ve mevcut ay evrenine bağlıdır. Benim için (ve etrafımdaki çoğu insan), döngü oldukça küçükse derin yuvalardan kurtulmak için bir goto kullanmak iyi olur ve alternatif, sadece döngü koşulunun kırılması için sunulan bir bool değişkeninin gereksiz bir kontrolü olacaktır. Bu daha çok vücudun yarısını kırmak istediğinizde veya en içteki döngüden sonra boolü de kontrol etmeniz gereken bir kod olduğunda.
PlazmaHH

1
@JohnWu gotoaslında bir C # anahtar ilk kod içinde herhangi bir kod yürütüldükten sonra başka bir duruma düşmesi için gereklidir. Yine de goto kullandığım tek durum bu.
Dan Lyons

Yanıtlar:


33

Bir go-to ifadesine görünen ihtiyaç , döngüler için kötü koşullu ifadeler seçmenizden kaynaklanır .

Dış halkanın uzun süre devam etmesini i < 10ve en içteki halkanın da uzun süre devam etmesini istediğinizi belirtiyorsunuz j > 0.

Ama gerçekte istediğiniz bu değil, döngülere değerlendirmelerini istediğiniz gerçek durumu söylemediniz, sonra mola veya git kullanarak çözmek istiyorsunuz.

Döngülere başlamak için gerçek niyetlerinizi söylerseniz, molalara veya gitmeye gerek yoktur.

bool alsoThis = true;
for (i = 0; i < 10 && alsoThis; i++)
{    
    for (j = 10; j > 0 && alsoThis; j--)
    {
        if (j == i)
        {
            alsoThis = false;
        }
    }
}

Diğer cevaplar hakkındaki yorumlarıma bakın. Orijinal kod idış döngünün sonuna yazdırılır , bu nedenle artışın kısa devresi, kodun% 100 eşdeğer görünmesi için önemlidir. Cevabınızdaki hiçbir şeyin yanlış olduğunu söylemiyorum, sadece döngüyü derhal kırmanın kodun önemli bir yönü olduğunu belirtmek istedim .
pswg

1
@pswg OP'nin sorusundaki idış döngünün sonunda herhangi bir kod yazdırması görmüyorum .
Tulains Córdova

OP buradaki sorumun koduna atıfta bulundu , ancak o kısmı dışarıda bıraktı. Seni küçümsemedim, BTW. Yanıt, döngü koşullarının doğru seçilmesi konusunda tamamen doğrudur.
pswg

3
@pswg Gerçekten bir gerçekten bir goto'yu haklı çıkarmak istiyorsanız, bir tane ihtiyaç duyan bir problemi cihazlayabilirsiniz, diğer yandan, yirmi yıldır profesyonel olarak programlıyorum ve ihtiyacı olan tek bir gerçek dünya problemi yok bir.
Tulains Córdova

1
Kodun amacı, bu gotoşekilde kullanıldığı anlaşılan bir soru ortaya çıkarsa bile, ancak daha iyi olmayan break(n)dillerin temel eylemini göstermek için geçerli bir kullanım durumu sağlamak değildi (eminim daha iyi olanlar var). t destekleyin.
pswg

34

Bu durumda, kodu ayrı bir rutine ve sonra da yalnızca returniç döngüden yeniden düzenleyebilirsiniz . Bu, dış döngüden kopma ihtiyacını ortadan kaldıracaktır:

bool isBroken() {
    for (i = 0; i < 10; i++)
        for (j = 10; j > 0; j--)
            if (j == i) return true;

    return false;
}

2
Yani evet, gösterdiğiniz şey iç içe döngülerden kopmak için gereksiz derecede karmaşık bir yoldur, ancak hayır, bir goto'yu daha çekici hale getirmeyen bir refactor yolu var.
Roger

2
Sadece bir not: orijinal kodda idış halkanın bitiminden sonra yazdırılır . Yani gerçekten yansıtmak iönemli bir değerdir, burada return true;ve return false;her ikisi de olmalıdır return i;.
pswg

+1, bu cevabı göndermek üzereydi ve bunun kabul edilen cevap olması gerektiğine inanıyorum. Kabul edilen cevabın kabul edildiğine inanamıyorum çünkü bu yöntem sadece iç içe döngü algoritmalarının belirli bir alt kümesi için çalışıyor ve kodu çok daha temiz hale getirmiyor. Bu yanıttaki yöntem genellikle işe yarar.
Ben Lee

6

Hayır, a'nın gotogerekli olduğunu düşünmüyorum . Bu şekilde yazarsanız:

bool broken = false;
saved_i = 0;
for (i = 0; i < 10 && !broken; i++)
{
    broken = false;
    for (j = 10; j > 0 && !broken; j--)
    {
        if (j == i)
        {
            broken = true;
            saved_i = i;
        }
    }
}

Her &&!brokeniki döngüye de sahip olduğunuzda , her iki döngüden de kurtulmanızı sağlar i==j.

Bana göre bu yaklaşım tercih edilir. Döngü koşulları son derece açıktır: i<10 && !broken.

Çalışan bir sürüm burada görülebilir: http://rextester.com/KFMH48414

Kod:

public static void main(String args[])
{
    int i=0;
    int j=0;
    boolean broken = false;
    for (i = 0; i < 10 && !broken; i++)
    {
        broken = false;
        for (j = 10; j > 0 && !broken; j--)
        {
            if (j == i)
            {
                broken = true;
                System.out.println("loop breaks when i==j=="+i);
            }
        }
    }
    System.out.println(i);
    System.out.println(j);
}

Çıktı:

i == j == 1 olduğunda döngü kesiliyor
2
0

Hatta neden kullanıyorsunuz brokenilk örnekteki hiç ? Sadece iç döngüde kırılma kullanın. Ayrıca, kesinlikle kullanmanız gerekiyorsa broken, neden döngülerin dışında ilan ettiniz ? Sadece ilan içeridei döngü. whileDöngü gelince , lütfen bunu kullanmayın. Dürüst olmak gerekirse , goto versiyonundan daha az okunabilir olmayı başardığını düşünüyorum .
luiscubal

@luiscubal: (switch-case hariç, ancak bu konuda) deyimini brokenkullanmayı sevmediğim için adlı bir değişken kullanılır break. Ne zaman ALL döngülerini kırmak istemeniz durumunda dış döngü dışında bildirilir i==j. Haklısın, genel olarak, brokensadece en dıştaki döngüden önce ilan edilmesi gerekiyor.
Hayal kırıklığına

Güncellemenizde, i==2döngünün sonunda. Başarı koşulu, a değerine% 100 eşdeğer ise, artışı kısa devre yapmalıdır break 2.
pswg

2
Ben şahsen tercih ediyorum broken = (j == i)ve eğer dil izin veriyorsa, kırık bildirimi dış döngü başlatma içine koyarak. Bu özel örnek için bunları söylemek zor olsa da, tüm döngü bir no-op (hiçbir şey döndürmez ve yan etkisi yoktur) ve bir şey yaparsa, muhtemelen if içinde yapar (yine de broken = (j ==i); if broken { something(); }daha iyisini seviyorum) Şahsen)
Martijn

@Martijn Orijinal kodumda idış halkanın bitiminden sonra yazdırılıyor . Başka bir deyişle, gerçekten önemli olan tek düşünce, dış döngüden tam olarak koptuğu zamandır.
pswg

3

Kısa cevap "duruma bağlıdır". Kodunuzun okunabilirliği ve anlaşılırlığı konusunda seçim yapmayı savunuyorum. Goto yolu, durumu düzeltmekten daha okunaklı olabilir veya tersi de olabilir. En az sürpriz ilkesini, mağazada / projede kullanılan kılavuzu ve kod tabanının tutarlılığını da göz önünde bulundurabilirsiniz. Örneğin, anahtarı bırakmaya veya hata işleme için Linux çekirdeği kod tabanında yaygın bir uygulamadır. Ayrıca bazı programcıların kodunuzu okumada sorun yaşayabileceğini unutmayın (ya "goto kötüdür" mantrasıyla yükseltilmiş ya da sadece öğretmenleri öyle düşündüğü için öğrenilmemiştir) ve birkaçı "sizi yargılayabilir".

Genel olarak konuşursak, özellikle zıplama çok uzun değilse, aynı işlevde daha ileri gitmek bir goto genellikle kabul edilebilir. Yuvalanmış bir döngüden ayrılma durumunuz bilinen "o kadar kötü değil" goto örneğidir.


2

Sizin durumunuzda, goto cazip görünen bir iyileştirmeye yeterlidir, ancak kod tabanınızda onları kullanmaya karşı kuralı ihlal etmeye değecek bir gelişme değildir.

Gelecekteki yorumcunuzu düşünün: "Bu kişi kötü bir kodlayıcı mı yoksa bu aslında goto'nun buna değer olduğu bir durum mu? Bunu kendim için kararlaştırmak veya araştırmak için 5 dakika ayırmama izin verin. internet." Tamamen goto kullanamadığınızda neden gelecekteki kodlayıcınıza bunu yapasınız ki?

Genel olarak bu zihniyet tüm "kötü" kodlar için geçerlidir ve hatta onu tanımlar: eğer şimdi çalışıyorsa ve bu durumda iyi ise, ancak doğru olduğunu doğrulamak için çaba sarf eder, bu dönemde bir goto her zaman yapar, o zaman yapma yap.

Bununla birlikte, biraz daha dikenli durumlardan birini ürettiniz. Yapabilirsin:

  • her iki döngüden kurtulmak için boole bayrağı gibi daha karmaşık kontrol yapıları kullanın
  • iç döngünüzü kendi rutininin içine koyun (muhtemelen ideal)
  • her iki döngüyü de rutin içine koyun ve kırmak yerine geri dönün

Bir çift döngünün o kadar tutarlı olmadığı bir durum yaratmak benim için çok zor, zaten kendi rutini olmamalı.

Bu arada, bunun en iyi tartışması Steve McConnell'in Code Complete'idir . Bu sorunları eleştirel bir şekilde düşünmekten hoşlanıyorsanız, okumasına, önemsizliğine rağmen örtbas etmenizi şiddetle tavsiye ederim.


1
Kodu okumak ve anlamak iş programcılarımızdır. Kod tabanınızın doğası olmadığı sürece sadece basit kod değil. Anlaşılan maliyet nedeniyle gotos kullanımına karşı genel (kural değil) istisnalar vardır ve her zaman olacaktır. Fayda ağır bastığında maliyet, gitmesi gereken zamandır; yazılım mühendisliğindeki tüm kodlama kararlarında olduğu gibi.
dietbuddha

1
@dietbuddha, dikkate alınması gereken kabul edilmiş yazılım kodlama kurallarını ihlal etmenin bir maliyeti vardır. Bu kontrol yapısı bir duruma uygun olsa bile, bir programda kullanılan kontrol yapılarının çeşitliliğini arttırmanın maliyeti vardır. Kodun statik analizinin kırılmasının maliyeti vardır. Bunların hepsi buna değerse, o zaman kiminle tartışacağım.
djechlin

1

TLD; DR: Spesifik olarak hayır, genel olarak evet

Kullandığınız spesifik örnek için, diğer birçok kişinin işaret ettiği aynı nedenlerle hayır diyebilirim. Kodu, ihtiyacını ortadan kaldıran, eşdeğer derecede iyi bir forma kolayca yeniden düzenleyebilirsiniz goto.

Genel olarak, yasaklanma goto, anlaşılan doğasına gotove onu kullanma maliyetine dayanan bir genellemedir . Yazılım mühendisliğinin bir kısmı uygulama kararları vermektir. Bu kararların gerekçelendirilmesi, maliyetin faydalara karşı tartılması ve faydaların maliyetten daha ağır basmasıyla, uygulamanın gerekçelendirilmesi ile gerçekleşir. Tartışma hem maliyet hem de fayda gibi farklı değerlemelerden kaynaklanır.

Mesele şu ki goto, a'nın haklı olduğu her zaman istisnalar olacaktır , ancak sadece bazıları için farklı değerlemeler nedeniyle. Yazık ki birçok mühendis, sadece amacı anlamaya yönelik cehalet teknikleri hariç tutuyor, çünkü nedenini anlamak için zaman ayırmak yerine internette okuyorlar.


Goto beyanında maliyeti daha fazla açıklayan herhangi bir makale önerebilir misiniz?
Thys

-1

Bu durum bir istisnayı haklı bulmuyor, çünkü bu yazılabilir:

def outerLoop(){
    for (i = 0; i < 10; i++) {
        if( broken?(i) )
          return; // broken
    }
}

def broken?(i){
   for (j = 10; j > 0; j--) {
        if (j == i) {
            return true; // broken
        }
   }
   return false; // not broken
}
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.