Ara ver ve devam et kötü programlama uygulamaları?


191

Patronum şüphesiz kötü programcıların breakve continuedöngüler içinde kullandıklarından bahseder .

Onları her zaman kullanırım çünkü anlamlıdırlar; size ilhamını 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 işlevin koşulların doğru olduğunu kontrol etmesi, ardından gerçek işlevselliği yürütmesidir. IMO aynı 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;
    ...
}

Bunun okunmayı ve hata ayıklamayı kolaylaştıracağını düşünüyorum; ama ayrıca bir dezavantaj göremiyorum.


2
Hangisinin ne yaptığını unutmak çok fazla zaman almaz.

57
Hayır. Hiçbiri de gitmiyor. Bunları ne zaman kullanacağınızı bilmek anahtardır. Bunlar araç kutusundaki araçlardır. Açık ve özlü kod sağladıklarında bunları kullanırsınız.
orj

67
Bu tarz kodlama için desteğimi yeterince kuvvetle söyleyemem. Birden fazla iç içe koşullandırma düzeyi bu yaklaşımdan çok daha kötüdür. Genelde kodlama tarzı konusunda militan değilim, ancak bu benim için neredeyse bir anlaşma.
Emil H

9
Belli ki patronun (yeterince) kod yazmıyor. Öyle olsaydı goto, bazı durumlarda tüm anahtar kelimelerin (evet, hatta ) yararlı olduğunu bilirdi.
Sakisk

57
Kötü programcılar ara kullanır ve devam eder , iyi programcıların kullanmadığı anlamına gelmez. Kötü programcılar eğer ve aynı zamanda kullanırlar.
mouviciel

Yanıtlar:


240

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

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


6
@Klaim: Birkaç çıkış noktası olan herhangi bir rutinin zayıf faktörlü bir rutin olduğu söylenebilir. Düzgün bir faktörü olan bir rutin sadece bir şeyi ve bir şeyi yapmalıdır.
bit-twiddler

79
@ bit-twiddler: Bu çok C-ish zihniyet. Geçici bir değişken daha sonra değiştirilebilir, bu nedenle buradan 20 satır aşağı olan tek bir yazım hatası, dikkatlice hazırlanmış sonucu silebilir. Ancak hemen geri dönüş (veya mola veya devam) son derece açık: Şimdi okumaya son verebilirim çünkü daha aşağıdan değiştirilemeyeceğini biliyorum . Bu benim küçük beynim için iyi, gerçekten kodda dolaşmayı kolaylaştırıyor.
Matthieu M.

15
@Mattthieu katılıyorum. Bloğun amacına uygun bir sonuç aldığınızda bloktan çıkın.
Evan Plaice

8
@ bit-twiddler - tek çıkış noktası ilkesi, tek sorumluluk ilkesinden ayrıdır. Kontrolün her zaman WRT'yi tek sorumluluk olarak kullanmaktan ayrı olduğunu kabul etmiyorum. Aslında “tek sorumluluk” her zaman öznel bir terim olarak beni vurur. Örneğin, ikinci dereceden bir çözücüde, ayrımcıyı hesaplamak ayrı bir sorumluluk mu olmalı? Yoksa tüm ikinci dereceden formül tek bir sorumluluk olabilir mi? Bunun, ayrımcı için ayrı bir kullanımınızın olup olmadığına bağlı olduğunu savunuyorum - aksi halde, ayrı bir sorumluluk olarak kabul etmek muhtemelen aşırıdır.
Steve314

10
Bu cevap zor bir kural değil, bir kuraldır. Çoğu durumda çalışır, bağlamınız için mantıklı olursa kırmaktan çekinmeyin.
Klaim

87

Donald Knuth'un 1974 tarihli Yapısal Programlama bölümünü okuyarak go to, yapısal olarak arzu edilen çeşitli kullanımlarını tartıştığı İfadelere bakabilirsiniz. Bunlar, eşdeğer breakve continueifadeleri içerir ( go toiçerisindeki kullanımların çoğu, daha sınırlı yapılar halinde geliştirilmiştir). Patronunuz Knuth'a kötü bir programcı diyecek tür mü?

(Verilen örnekler beni ilgilendirir. Tipik olarak breakve continueherhangi bir kod parçasından tek bir girişi ve bir çıkışını seven ve bu tür bir kişi birden fazla returnifadeye bayılan insanlar tarafından beğenilmez .)


8
Tek bir giriş ve çıkış noktasına sahip olmak üzere işlev ve prosedürleri seven insanların çoğu Pascal'da büyüdü. Pascal öğrendiğim ilk dil değildi, ama bu günkü kodu nasıl yapılandırdığım üzerinde derin bir etkisi oldu. İnsanlar her zaman Java kodumu okumanın ne kadar kolay olduğu konusunda yorum yapar. Bunun nedeni, çoklu çıkış noktalarından ve kod beyanlarını karıştırmaktan kaçınmam. Bir yöntemde kullanılan her yerel değişkeni, yöntemin en üstünde belirtmek için elimden geleni yapıyorum. Bu uygulama, beni metotları özde tutmaya zorlayarak zorlukla mücadele eder.
bit-twiddler

5
Pascal'ın da iç içe geçmiş işlevleri vardı. Sayin '...
Shog9

6
Hatırladığım kadarıyla, günün geri kalanında, insanların işlevlerde çoklu geri dönüş ifadeleri sevmemesinin temel nedeni, hata ayıklayıcıların bunları düzgün bir şekilde ele almamasıydı. Bir fonksiyonun sonuna bir kesme noktası koymak gerçek bir acıydı, ancak daha önce bir geri dönüş ifadesi nedeniyle asla vurmadınız. Bugünlerde kullandığım her derleyici için bu artık bir sorun değil.
Dunk,

28
@ bit-twiddler: Bundan o kadar emin değilim. Bugün hala Pascal kullanıyorum ve genellikle "tek giriş tek çıkış" ya da en azından bunun tek çıkış kısmını kargo kültü programlaması olarak görüyorum. Sadece düşünün Break, Continueve Exitbenim alet araçları olarak; Onları kodun takip edilmesini kolaylaştıracak yerlerde kullanıyorum ve bunları okumayı zorlaştıracak yerlerde kullanmıyorum.
Mason Wheeler,

2
@ bit-twiddler: bunun için. Ayrıca, ekrana kolayca oturan bloklara düştüğünüzde, çoklu çıkış noktalarının çok daha az zahmetli hale geldiğini de ekleyeceğim.
Shog9

44

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

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

Eğer işleviniz çok kısaysa, tek bir ilmekiniz varsa veya en kötü iki iç içe ilmeğiniz varsa ve ilmek gövdesi çok kısaysa, o zaman a breakveya a'nın ne yaptığı çok açıktır continue. Birden fazla returnifadenin ne yaptığı da açıktır .

Bu konular Robert C. Martin tarafından "Temiz Kod" te ve Martin Fowler tarafından "Yeniden Düzenleme" de ele alınmıştır.


12
"İşlevlerinizi küçük yapın. O zaman onları küçültün" -Robert C. Martin. Bunun şaşırtıcı derecede iyi çalıştığını buldum. Ne yaptığını açıklayan bir yorum gerektiren bir işlevde bir kod bloğunu her gördüğünüzde, onu açıklayıcı bir adla ayrı bir işleve sarın. Sadece birkaç satır olsa ve sadece bir kez kullanılsa bile. Bu uygulama, kesme / devam ya da çoklu iade ile ilgili sorunların çoğunu ortadan kaldırır.
Dima,

2
@Mikhail: Döngüsel karmaşıklık genel olarak SLOC ile kuvvetli bir şekilde ilişkilidir, bu da tavsiyenin "uzun işlevler yazmama" için basitleştirilebileceği anlamına gelir.
John R. Strohm

3
Tek bir çıkış noktası fikri yaygın olarak yanlış yorumlanmıştır. Bir zamanlar, işlevler arayanları geri vermek zorunda değildi. Başka bir noktaya dönebilirlerdi. Bu genellikle assembly dilinde yapıldı. Fortran'ın bunun için özel bir yapısı vardı; ampersand tarafından verilen bir ifade numarasını iletebilir CALL P(X, Y, &10)ve hata olması durumunda, işlev çağrının noktasına geri dönmek yerine kontrolü bu ifadeye iletebilir.
kevin cline

Örneğin, Lua ile görüldüğü gibi kevincline.
Qix

1
@cjsimon var. Sadece fonksiyonlar küçük olmamalıdır. Sınıflar da küçük olmalıdır.
Dima

39

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

Ara ve sık kullanmaya devam etmek, kodun takip edilmesini zorlaştırır. Fakat eğer bunları değiştirmek, kodu takip etmeyi zorlaştırırsa, bu kötü bir değişiklik.

Verdiğiniz örnek kesinlikle ara vermenin ve devam etmenin daha zarif bir şeyle değiştirilmesi gereken bir durumdur.


Evet, çıkacak vakaların örneklerini sağlamak için şartların sayısını abarttım.
Mikhail

9
Önerdiğiniz değiştirme kodunun bir örneği nedir? Bekçi ifadelerinin oldukça makul bir örneği olduğunu düşündüm.
simgineer

Bana öyle geliyor ki, "mümkün olan en açık çözüm" her zaman ... mümkün mü? “En net” bir çözüm nasıl olmaz? Ama o zaman mutlak olanlardan biri değilim, o yüzden belki de haklısınız.

@nocomprende Neye ulaştığınızdan emin değilim. Buradaki “mümkün”, en iyi çözümün mevcut olmadığını göstermez - yalnızca bu mükemmel, nihai açıklık bir şey değildir. Sonuçta özneldir.
Matthew

22

Çoğu insan bunun kötü bir fikir olduğunu düşünür çünkü davranış kolayca tahmin edilebilir değildir. Eğer kodu okuyorsanız ve while(x < 1000){}x> = 1000 olana kadar çalışacağını varsayarsınız ... Fakat ortada bir mola varsa, o zaman bu doğru olmaz, bu yüzden gerçekten kendinize güvenemezsiniz. döngü ...

Sonuçta sorunsuz: İnsanların GOTO sevmiyorum aynı neden olabilir iyi kullanılabilir, ama aynı zamanda kod bölümden bölüme rastgele atlar godawful spagetti kod, yol açabilir.

Kendim için, birden fazla şartı bozan bir döngü yapacak olsaydım, ayrılmam gerektiğinde while(x){}X'i yanlışa çevirirdim. Nihai sonuç aynı olacaktır ve kodu okuyan herkes X'in değerini değiştiren şeylere daha yakından bakmayı bilirdi.


2
+1 çok iyi dedi ve while(notDone){ }yaklaşım için +1 (başka bir şey yapabilirsem) .
SinirliFormsDesigner ile

5
Mikhail: Moladaki sorun, döngü için son koşulun hiçbir zaman tek bir yerde açıkça ifade edilmemesidir. Bu, döngüden sonraki post-koşulu tahmin etmeyi zorlaştırır. Bu önemsiz durumda (> = 1000) zor değil. Birçok if ifadesi ve farklı iç içe geçme seviyeleri ekleyerek döngünün post-koşulunu belirlemek çok, çok zor olabilir.
S.Lott

S.Lott, çiviyi tamamen kafasına vurdu. Yinelemeyi kontrol eden ifade, yinelemeye devam etmek için yerine getirilmesi gereken her koşulu içermelidir.
bit twiddler

6
Değiştirme break;ile x=false;kodunuzu daha net yapmaz. Bu ifadeyi yine de aramak zorundasınız. Ve bunun durumunda, daha fazla x=false;vurulmadığını kontrol etmek zorunda kalacaksınız x=true;.
Sjoerd

14
İnsanlar "x'i görüyorum ve y'yi varsayıyorum, ama z yaparsanız bu varsayım" kabul etme eğiliminde değil bu yüzden bu aptalca varsayımı yapma "demediğinde. Pek çok insan bunu " while (x < 1000)1000 kez çalışacağını varsaydığımı gördüğümde" kolaylaştıracaktı . Eh, orada birçok olsa bile, bu yanlış nedenleri xbaşlangıçta sıfırdır. Örneğin, kim xdöngü sırasında tam olarak bir kez artırıldığını ve hiç bir şekilde değiştirilmediğini söylüyor ? Kendi varsayımınız için bile, sadece bir şeyler x >= 1000olduğu için, döngünün biteceği anlamına gelmez - durum kontrol edilmeden önce menzile girebilir.
Steve314

14

Evet, break ifadesi olmadan programları yeniden yazabilirsiniz (veya aynı şeyi yapan döngülerin ortasından döndürür). Ancak, her ikisinin de programı anlamayı zorlaştırdığı ek değişkenler ve / veya kod çoğaltması yapmanız gerekebilir. Pascal (programlama dili) özellikle acemi programcılar için bu nedenle çok kötüydü. Patronun temel olarak Pascal'ın kontrol yapılarında program yapmanı istiyor. Linus Torvalds ayakkabının içinde olsaydı, muhtemelen patronuna orta parmağını gösterirdi!

Kosaraju’nun 1973’e dayanan ve 1974’ten bu yana Knuth’un (daha fazla) ünlü makalesinde değinilen kontrol yapıları hiyerarşisi olarak adlandırılan bir bilgisayar bilimi sonucu var. .) ne S. Rao Kosaraju 1973 yılında kanıtladı o derinlik çok düzeyli sonları olan tüm programları yeniden yazmak mümkün değildir olmasıdır n daha mola derinliği az olan programlara n ekstra değişkenleri tanıtan olmadan. Diyelim ki bu sadece teorik bir sonuç. (Sadece bir kaç ekstra değişken ekleyelim mi ?! Kesinlikle patronunuzu memnun etmek için bunu yapabilirsiniz ...)

Yazılım mühendisliği perspektifinden çok daha önemli olan, Eric S. Roberts'ın Döngü Çıkışları ve Yapısal Programlama: Tartışmayı Yeniden Açma başlıklı 1995 tarihli makalesidir ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Roberts ondan önce başkaları tarafından yürütülen deneysel çalışmaları özetlemektedir. Örneğin, CS101 tipi bir grup öğrenciden bir dizide sıralı bir arama uygulayan bir fonksiyon için kod yazması istendiğinde, çalışmanın yazarı, bir ayrılma / geri dönüş / goto kullanan kişi hakkında eleman bulunduğunda sıralı arama döngüsü:

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

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

Sorunu açık bir şekilde kullanmadan çözmeye çalışan öğrenciler, for döngüsünden geri dönüşü çok daha az başardı: Bu stratejiyi deneyen 42 öğrenciden sadece 7'si doğru çözümler üretmeyi başardı. Bu rakam% 20'den az bir başarı oranını temsil ediyor.

Evet, CS101 öğrencilerinden daha deneyimli olabilirsiniz, ancak break deyimini kullanmadan (veya döngülerin ortasından eşit olarak geri döndüğünüzde), sonunda mantıklı bir şekilde yapılandırılmış olarak ekstra mantık açısından yeterince kıllı olan kod yazacaksınız. Patronunuzun kodlama stilini izlemeye çalışırken muhtemelen kendiniz birinin kullandığı değişkenler ve kod çoğaltması buna mantık hataları koyacaktır.

Burada Roberts’ın kağıdının ortalama bir programlayıcı için çok daha erişilebilir olduğunu söyleyeceğim, bu yüzden Knuth’lardan daha iyi bir ilk okuma. Aynı zamanda daha kısa ve daha dar bir konuyu kapsıyor. Muhtemelen CS tipinden ziyade yönetim olsa bile bunu patronunuza önerebilirsiniz.


2
Yapısal programlama uzun yıllar boyunca benim yaklaşımım olmasına rağmen, son birkaç kişi ilk fırsatta tamamen açık çıkışları kullanmaya başladı. Bu işlem daha hızlı çalışmayı sağlar ve sonsuz döngülere (asılı) neden olan mantık hatalarını neredeyse ortadan kaldırır.
DocSalvager

9

Bu kötü uygulamaların ikisini de kullanmayı düşünmüyorum, ancak aynı döngü içinde onları çok fazla kullanmak döngüde kullanılan mantığı yeniden düşünmeyi sağlamalıdır. Onları dikkatli kullanın.


:) evet, verdiğim örnek kavramsaldı
Mikhail

7

Verdiğiniz örneğin ara vermesine 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;
   }

Örnekteki 4 satırla ilgili 'sorunum' hepsi aynı seviyede olmaları, ancak farklı şeyler yapmalarıdır: Bazıları ara, bazıları devam eder ... Her satırı okumalısınız.

İç içe yaklaşımımda, ne kadar derine giderseniz, kod o kadar “kullanışlı” olur.

Ancak, derinlerde bir döngüyü durdurmak için bir neden bulursanız (birincil koşul dışında), bir mola veya geri dönüşün kullanımı olur. Üst seviye bir durumda test edilecek ekstra bir bayrak kullanılmasını tercih ederim. Mola / geri dönüş daha doğrudandır; başka bir değişkeni ayarlamaktan daha iyi bir niyet olduğunu belirtir.


1 Fakat gerçekte <karşılaştırmalar olması gerekiyor <=OP çözüm eşleşecek
El Ronnoco

3
Çoğu durumda, biri o kadar derinse, akış kontrolünü yönetmek için ara ver / geri kullanması gerekiyorsa, birinin işlevi / yöntemi çok karmaşıktır.
bit-twiddler

6

"Kötülük", onları nasıl kullandığınıza bağlıdır. SADECE döngü yapılarında molaları kullanıyorum, SADECE bir algoritmanın yeniden yapılandırılması ile kurtarılamayan çevrimleri kurtardığı zaman. Örneğin, belirli bir özellik için true değerine sahip bir öğeyi arayan bir koleksiyonda gezinme. Bilmeniz gereken tek şey, öğelerden birinin bu özelliği true olarak ayarlamış olması durumunda, bu sonucu elde ettiğinizde, bir döngüyü uygun şekilde sonlandırmak iyidir.

Bir mola kullanılması, kodun okunmasını, çalıştırılmasının daha kısa sürmesini ya da işlemlerde belirgin bir şekilde tasarruf edilmesini kısaltıyorsa, bunları kullanmamak en iyisidir. Beni takip eden herkesin koduma kolayca bakabildiğinden ve ne olup bittiğini anlayabilmesinden emin olmak için mümkün olduğunda "en düşük ortak payda" ya kodlama eğilimindeyim (bu konuda her zaman başarılı olamam). Kırılmalar bunu azaltıyor çünkü tek giriş / çıkış noktaları oluşturuyorlar. Kötüye kullanıldıkları için, "tomo" ifadesi gibi davranırlar.


Her iki noktaya katılıyorum! İkinci konuya gelince - Sanırım asıl yazımı takip etmek daha kolay, çünkü İngilizce gibi okuyor. 1 ifadesinde koşulların bir birleşimi varsa, o zaman doğru gerçekleşmesi için ne olması gerektiğine karar vermek neredeyse bir deşifre olur.
Mikhail

@Mikhail: Sağladığınız örnekler belirli gerçekçilik için biraz özgecildir. Gördüğüm gibi, bu örnekler açık, özlü, okunması kolay. Çoğu ilmek böyle değildir. Çoğu döngü, gerçekleştirdikleri başka bir mantığa ve potansiyel olarak çok daha karmaşık koşullara sahiptir. Bu, kopma / devam etmenin en iyi kullanım olamayacağı durumlardadır, çünkü okunduğunda mantığı kirletir.
Joel Etherton

4

Kesinlikle hayır ... Evet, kullanımı gotokötü, çünkü programınızın yapısını bozuyor ve kontrol akışını anlamak çok zor.

Ancak bu günlerde olduğu gibi breakve continuekesinlikle kesinlikle gerekli olan ifadelerin kullanılması ve hiç de kötü programlama uygulaması olarak kabul edilmemektedir.

Ve ayrıca o kadar zor değil kullanımında kontrol akışını anlamak breakve continue. Gibi yapılarda açıklamada kesinlikle gereklidir.switchbreak


2
1981'de C'yi ilk öğrendiğimden beri "devam" ı kullanmadım. Bu, gereksiz bir dil özelliğidir.
bit-twiddler

12
Bu durumlarda devam etmeyi tercih ederim çünkü kodumun ok kodu olmadığından emin olur. Ok kodundan goto tip ifadelerden daha çok nefret ediyorum. Ayrıca, "Bu ifade doğruysa, bu döngünün kalanını atla ve sonraki yinelemeye devam et" olarak da okudum. Bir for döngüsünün başındayken çok faydalıdır (döngü sırasında daha az faydalıdır).
jsternberg

@jsternberg Kazanmak için! :-)
Notinlist

3

Temel kavram, programınızı anlamsal olarak analiz edebilmekten geliyor. 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ütmeyi başarabiliyor.

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


Ben müdür bunları kullanmak için zorunlu bir projede çalışırken, ben kullanımını hatırlamak için bir akış şemasını kullanmak zorunda kaldı ve birkaç çıkış yolları, ... daha kafa karıştırıcı inci kodu yaptın
umlcat

"Açıkçası" yorumunuz sözdizimseldir - "sonraki" bir perl şeydir; normal diller "devam et" i kullanarak "bu döngüyü atla" anlamına gelir
Mikhail

@Mik, belki "bu yinelemeyi atla" daha iyi bir açıklama olabilir. Saygılarımla, Dr. IM Pedantic
Pete Wilson

@Mikhail: Tabii. Ancak, biri birçok dilde çalıştığında, sözdizimi diller arasında yaygın olmadığında hatalara yol açabilir.
Paul Nathan,

Ancak matematik programlama değildir . Kod matematikten daha anlamlı. Ben olsun (Yani. Nerede mola / dönüş kodu daha iyi performans yapabilirsiniz) Tek girişli / tek çıkış akış şemaları daha güzel ama ne gider bakmak bulunabilmesi?
Evan Plaice

3

İkinci kod parçacığını değiştiririm

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

Herhangi bir açıklık sebebi için değil - aslında bunun daha kolay olduğunu ve birinin neler olduğunu anlaması için daha kolay olduğunu düşünüyorum. Genel olarak konuşursanız, döngülerinizin koşullarının tamamen vücutta yayılmamış bu döngü koşullarında yer alması gerekir. Ancak okunabilirliğe yardımcı olabilecek breakve continueyardımcı olabileceği bazı durumlar vardır . ekleyebileceğimden breakdaha fazla moreso continue: D


Buna katılmıyorum, "Aslında bunu okumak daha kolay" diye düşünüyorum, bu yukarıdaki kodla aynı amacı yerine getiriyor. Şimdiye kadar bir kısa devre operatörü olarak 'mola' gerçekten düşünmedim ama mükemmel mantıklı. "Genel olarak, döngülerinizin koşullarının yalnızca bu döngü koşulları içinde olması gerekir" ifadesiyle ilgili olarak, döngü tanımına gerçekten koşullu mantık eklemenin bir yolu olmadığı için bir foreach döngüsünü işlerken ne yaparsınız?
Evan Plaice

@ Evan Bu koşul foreachbir koleksiyon için geçerli değildir, çünkü koleksiyondaki her öğeyi yineleyecektir. Bir fordöngü, şartlı bir son noktaya sahip olmaması gerektiği için benzerdir. Koşullu bir son noktaya ihtiyacınız varsa, o zaman bir whiledöngü kullanmanız gerekir .
El Ronnoco

1
@Evan Anladığım kadarıyla noktayı görüyorum - yani 'foreach döngüsünden çıkmanız gerekirse?' - breakBence döngüden sadece bir tane maksimum olmalı .
El Ronnoco

2

Patronuna katılmıyorum. Kullanılacak breakve continuekullanılacak uygun yerler var . Aslında uygulamaların ve istisnaların ele alınmasının modern programlama dillerine getirilmesinin nedeni, her sorunu sadece kullanarak çözememenizdir structured techniques.

Bir yandan notta, burada dini bir tartışma başlatmak istemiyorum, ancak kodunuzu bu şekilde daha okunaklı 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 yandan notta

Ben ( flag == true )şartlı kullanımlardan kişisel olarak hoşlanmam çünkü değişken zaten bir boole ise, boolenin değeri istediğiniz cevaba sahipse, olması gereken ek bir karşılaştırma sunuyorsunuz - tabii ki derleyicinizin alacağından emin değilseniz bu ekstra karşılaştırmayı uzağa optimize edin.


Bu soruyu yıllardır şaşırttım. Yolunuz ('if (flag) {...}') çok daha özlü ve belki de daha 'profesyonel' veya 'uzman' görünüyor. Ancak, bilirsiniz, bu genellikle bir okuyucunun / bakımcının yapının ne anlama geldiğini hatırlamak için kısa bir süre kestiği anlamına gelir; ve testin anlamından emin olmak için. Şu anda, 'if (flag == true) {...}' kullanıyorum, çünkü daha iyi belgeler gibi görünüyor. Gelecek ay? Quien Sabe?
Pete Wilson

2
@Pete - terim değildir veciz ama zarif . İstediğiniz kadar gözleme yapabilirsiniz, ancak okuyucunun / booleansağlayıcının ne olduğunu ya da kısa / zarif terminolojinin ne anlama geldiğini anlamadığından endişeleniyorsanız, belki daha akıllı
koruyucular

@Pete, ayrıca oluşturulan kod hakkındaki açıklamamın yanındayım. İfadenin boolean değerini değerlendirmeden önce bir bayrağı sabit bir değerle karşılaştırarak bir karşılaştırma daha yapıyorsunuz. Neden olması gerekenden daha zorlaştırıyor, bayrak değişkeni zaten istediğiniz değere sahip!
Zeke Hansell

+1 iyi iş. Kesinlikle eski örnekten daha zarif.
Evan Plaice

2

Patronunla aynı fikirdeyim. Kötüdürler çünkü yüksek döngüsel karmaşıklığa sahip yöntemler üretirler. Bu yöntemlerin okunması ve test edilmesi zor. Neyse ki kolay bir çözüm var. Döngü gövdesini “Devam Et” in “dönüş” olduğu ayrı bir yönteme çıkarın. "Geri dönüş" daha iyidir çünkü "geri döndükten" sonra biter - yerel devlet için endişelenmez.

"Break" için, "break" yerine "return" yerine döngünün kendisini ayrı bir yönteme çıkarın.

Ayıklanan yöntemler çok sayıda argüman gerektiriyorsa, bu bir sınıfı çıkarmanın bir göstergesidir - ya bir bağlam nesnesine toplayın.


0

Bence sadece çoklu döngüler içinde derinlere yuvalanmış bir problem. Hangi döngüye girdiğini bilmek zor. Ayrıca devam etmenin takip edilmesi zor olabilir, ama asıl acının molalardan kaynaklandığını düşünüyorum - mantığın izlenmesi zor olabilir.


2
Aslında, doğru şekilde girip girmediğinizi, bu ifadelerin anlamlarını içselleştirip çok uykulu olmadığını görmek kolaydır.

@delnan - bu bir çok varsayım;)
davidhaskins, 15.03

2
Evet, özellikle de sonuncusu.
Michael K

Yine de ciddi programlama için # 1 gereklidir, programcı denilen herkesten # 2 beklenir ve # 3 genel olarak oldukça faydalıdır;)

Bazı diller (yalnızca bildiğim komut dosyası) desteklemektedir break 2; diğerleri için, geçici bool bayrakları kullanıldığını tahmin ediyorum
Mikhail

0

Aşağıdaki örnekte olduğu gibi gizlenmiş 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)


Bu gibi vakaların işlev oluşturmasını tavsiye eder misiniz?
Mikhail

Evet ve mümkünse düz bir başlangıç. Bir yaptığımı görünce tekrarlamayı düşünüyorum, atlamayı simüle edecek bağlamı değil.
SuperBloup

oh katılıyorum, sadece nasıl yapacağınızı soruyordum :) Bilgisayar bilimcileri tekrar tekrar yapıyor
Mikhail

0

Bu tarzların hiçbirini sevmiyorum. İşte tercih edeceğim şey:

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 returnbir işlevi iptal etmek için kullanmayı sevmiyorum . Suistimali gibi geliyor return.

breakAyrıca kullanmak okumak her zaman net değildir.

Daha 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 yuvalanma ve karmaşık koşullar değişkenlere yeniden yansıtılır (gerçek bir programda daha iyi adlara sahip olmanız gerekir).

(Yukarıdaki kodların tümü sahte koddur)


11
Gerçekten yukarıda yazdıklarımın üzerine yerleştirme seviyesini mi tercih edersin?
Mikhail

@Mikhail: Evet, ya da koşulun sonucunu bir değişkene atardım. Anlaşmayı breakve mantığından daha kolay buluyorum continue. Anormal bir döngüye son vermek sadece garip hissettiriyor ve hoşuma gitmiyor.
SinirliFormsDesigner ile

1
İronik olarak, şartlarımı yanlış anladın. continueişlevi atlamak, sonraki döngüye geçmek anlamına gelir; "yürütmeye devam et" değil
Mikhail

@Mikhail: Ah. Sık kullanmıyorum ve okuduğumda anlamdan kafam karışıyor. Hoşlanmamamın başka bir nedeni. : P ... bana güncellemek için bir dakika ver
FrustratedWithFormsDesigner

7
Çok fazla yuvalama okunabilirliği yok eder. Ve bazen, ara vermeden / devam etmekten kaçınmak, mantıkınızı koşullu testlerinize ters çevirmenin gerekliliğini ortaya çıkarır, bu da kodunuzun ne yaptığını yanlış yorumlamaya yol açabilir - sadece söylüyorum '
Zeke Hansell

0

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

Pek çok güncel ana dil (Java, .NET (C # + VB), PHP, kendi dilini yaz) döngüleri atlamak için "break" ve "devam" ı kullanır. Her ikisi 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" kodunun daha kısa olduğunu ve genellikle "cümle" için "veya" foreach "cümle olarak" döndüğünü "unutmayın.

Her iki durumda da kodlama tarzı bir konudur. Bunları kullanmamayı tercih ediyorum , çünkü ayrıntılı stil kodu daha fazla kontrol etmeme izin veriyor.

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

Bazı geliştiriciler, gerekli olmadıklarını düşünebilirler, ancak varsayımsal, eğer onları çıkarmak zorunda kalırsak, "süre" yi ve "süre" yi ("kadar" tekrarlayın ");

Sonuç Onları kullanmamayı tercih etsem bile, bunun kötü bir programlama uygulaması olmadığını düşünüyorum.


Seçiciliğim için üzgünüm, ancak ikinci örneğiniz, anahtar bulunamadığında çıktısını kaçırıyor (tabii ki o kadar kısa görünüyor).
SinirliFormsDesigner ile

2
ilk örneğin sadece anahtar listedeki son anahtarsa ​​işe yaradığından bahsetmeyin.
Mikhail

@FormsDesigner KESİNLİKLE. Neden bu yöntemin tercih edildiğini göstermek için ambala koydum ;-)
umlcat

ancak, farklı anlambilimde iki rutininiz var; bu nedenle, mantıksal olarak eşdeğer değildir.
bit-twiddler

2
ikinci örneğinizde, biri sözdizimsel, diğeri mantıklı olmak üzere iki hata vardır. 1. Yineleyici, for döngüsünün kapsamı dışında bildirilmediğinden derlenmez (bu nedenle dize çıktısında bulunmaz). 2. Yineleyici döngünün dışında bildirilmiş olsa bile, anahtar koleksiyonda bulunmazsa, dizge çıktısı listedeki son öğenin anahtarını yazdırır.
Evan Plaice

0

Buna karşı continueve breakilke olarak değilim , ancak çok daha iyi bir şeyle değiştirilebilecek çok düşük seviyeli yapılar olduklarını düşünüyorum.

Burada bir örnek olarak C # kullanıyorum, bir koleksiyon üzerinde yineleme yapmak istediğimizi düşünün, ancak yalnızca bazı yordamları yerine getiren öğeleri istiyoruz ve maksimum 100 yinelemeden 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 NEDEN temiz görünüyor. Bunu anlamak çok zor değil. Bence daha açıklayıcı olmaktan çok şey kazanabileceğini düşünüyorum. Aşağıdakilerle karşılaştırın:

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

Belki de Nerede ve Al çağrıları bu yöntemde olmamalıdır. Belki de bu filtreleme, koleksiyonun bu metoda geçirilmesinden ÖNCE yapılmalıdır. Her neyse, düşük seviyeli nesnelerden uzaklaşarak ve gerçek iş mantığına odaklanarak daha çok GERÇEKLEŞTİRDİĞİMİZ ile netleştik. Kodumuzu iyi tasarım uygulamalarına daha çok bağlı kalabilen uyumlu modüllere ayırmak daha kolay hale geliyor. üzerinde.

Kodun bazı kısımlarında hala düşük seviyeli şeyler var olacak, ancak bunu mümkün olduğunca gizlemek istiyoruz, çünkü bunun yerine iş sorunları hakkında akıl yürütmek için kullanabileceğimiz zihinsel enerjiyi alır.


-1

Kod Tamamlama , gotorutin veya döngüden döndürme ve kullanma hakkında hoş bir bölüme sahiptir .

Genelde kötü bir uygulama değil. breakya da continuetam olarak ne olacağını söyleyin. Ve buna katılıyorum.

Steve McConnell (Code Complete'in yazarı), çeşitli gotoifadeleri kullanmanın avantajlarını göstermek için neredeyse aynı örnekleri kullanır .

Bununla birlikte aşırı kullanım breakveya continuekarmaşık ve sürdürülemez bir yazılıma yol açabilir.

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.