GOTO hala zararlı mı? [kapalı]


282

Herkes Dijkstra'nın editöre Mektupları'nın farkındadır : zararlı olarak kabul edilen ifadeye gidin (ayrıca burada .html transkript ve burada .pdf) ve o zamandan beri mümkün olduğunca goto ifadesinden kaçınmak için zorlu bir itici güç olmuştur. Goto'yu sürdürülemez, genişleyen kod üretmek için kullanmak mümkün olsa da, yine de modern programlama dillerinde kalır . Şemadaki gelişmiş devam kontrol yapısı bile karmaşık bir goto olarak tanımlanabilir.

Hangi koşullar goto kullanımını garanti eder? Ne zaman kaçınmak en iyisidir?

Takip eden bir soru olarak: C, sadece mevcut yığın çerçevesi içinde değil, çağıran çerçevelerden herhangi birinde gitme olanağı sağlayan bir setjmp ve longjmp işlevi sağlar. Bunlar goto kadar tehlikeli kabul edilmeli midir? Daha tehlikeli?


Dijkstra, sorumlu olmadığı bu unvandan pişman oldu. EWD1308'in sonunda (ayrıca burada .pdf) yazdı:

Sonunda kayıt için kısa bir hikaye. 1968'de, ACM'nin İletişimleri, " goto ifadesi zararlı olarak kabul edildi " başlığı altında bir metin yayınladı , ancak daha sonraki yıllarda en çok üzüntü duyan, daha sık görmeyen yazarlar tarafından bir şablon haline gelerek şöhretimin temel taşı haline gelen başlık: "Dijkstra zararlı kabul edilen" başlıklı biri de dahil olmak üzere hemen hemen her X için "X zararlı kabul edildi" başlığı altında her türlü makaleyi görürdük. Ama ne oldu? " Git ifadesine karşı bir dava", yayınlanmasını hızlandırmak için editör bir" Editöre mektup "olarak değişti ve bu süreçte kendisine kendi icadının yeni bir başlığını verdi! Editör Niklaus Wirth'ti.

Dijkstra'nınkiyle eşleşecek olan bu konuyla ilgili iyi düşünülmüş klasik bir makale , Donald E. Knuth'un ifadelere gittiği Yapısal Programlama'dır . Her ikisini de okumak, bağlamın ve konuyla ilgili dogmatik olmayan bir anlayışın yeniden oluşturulmasına yardımcı olur. Bu makalede, Dijkstra'nın bu dava hakkındaki görüşü rapor edilmiştir ve daha da güçlüdür:

Donald E. Knuth: İnanıyorum ki böyle bir görüş sunarak aslında Dijkstra'nın fikirlerine keskin bir şekilde katılmıyorum, çünkü son zamanlarda aşağıdakileri yazdı: "Lütfen [hakkında çok dogmatik olduğuma inanma tuzağına düşmeyin ] açıklamada gidin. Ben programlama kavramsal sorunlar disiplin kodlama basit bir form tarafından, tek bir numarayla çözülebilir sanki diğerleri, bunun dışında bir din yapıyoruz rahatsızlık duygusu var! "


1
C # 'ın goto dijkstra'nın bahsettiği goto ile aynı şey değildir, tam da Dijkstra'nın konuştuğu nedenlerden dolayı. Bu goto hem eskisi kadar zararlı, hem de çok daha az gerekli, çünkü modern diller alternatif kontrol yapıları sağlıyor. C # goto son derece kısıtlıdır.
jalf

25
Gotos, netlik kazandıklarında iyidir. Uzun iç içe bir döngünüz varsa, "break" değişkenlerini ayarlamaktan ve siz çıkana kadar kopmaktan daha iyi olabilir.
Haziran'da simendsjo

28
4 derinlikte yuvalanmış bir döngünüz varsa (iyi bir şey değil), hepsinden kurtulmak için geçici değerlerin ayarlanması gerekir. Buradaki bir goto benim için çok daha açık ve IDE goto'nun nerede olduğunu kolayca göstermelidir. Bununla birlikte, goto kullanımı seyrek olmalı ve bence sadece kodu atlamak için aşağı doğru hareket et
simendsjo

7
Etiketli dokuz bin bir parçayı okumanızı öneririm goto.
Matti Virkkunen

5
Bir şey var açıkça kötü kullanmaktan daha goto: Birlikte yapılandırılmış programlama araçları hack bir uygulamaya goto.

Yanıtlar:


184

Aşağıdaki ifadeler genellemelerdir; istisnayı savunmak her zaman mümkün olsa da, genellikle (tecrübelerime ve mütevazi görüşüme göre) risk almaya değmez.

  1. Bellek adreslerinin (GOTO veya ham işaretçiler) kısıtlanmamış kullanımı, kolayca önlenebilir hatalar yapmak için çok fazla fırsat sağlar.
  2. Koddaki belirli bir "konuma" ulaşmanın daha fazla yolu varsa, sistemin o noktada ne olduğu konusunda daha az emin olabilirsiniz. (Aşağıya bakınız.)
  3. Yapısal programlama IMHO, "GOTO'lardan kaçınmak" ve kodun yapısının verilerin yapısıyla eşleşmesi hakkında daha azdır. Örneğin, yinelenen bir veri yapısı (örneğin dizi, sıralı dosya vb.) Doğal olarak tekrarlanan bir kod birimi tarafından işlenir. Yerleşik yapılara sahip olmak (örneğin, her biri için, her şey için, vb. İçin) programcının aynı klişe kod kalıplarını tekrarlamaktan kaçınmasını sağlar.
  4. GOTO düşük seviyeli uygulama detayı olsa bile (her zaman böyle değil!) Programcının düşünmesi gereken seviyenin altındadır. Kaç programcı kişisel çek defterlerini ham ikili dosyada dengeler? Kaç programcı, sadece bir veritabanı motoruna bir anahtar vermek yerine, diskteki hangi sektör için belirli bir kayıt içerdiğinden endişe duyuyor (ve tüm programlarımızı fiziksel disk sektörleri açısından gerçekten yazsaydık, işler kaç yoldan yanlış gidebilir)?

Yukarıdakilere ilişkin dipnotlar:

2. nokta ile ilgili olarak, aşağıdaki kodu göz önünde bulundurun:

a = b + 1
/* do something with a */

Koddaki "bir şey yap" noktasında a, daha büyük bir güvenle söyleyebiliriz b. (Evet, tam sayı taşmayan tamsayı taşması olasılığını görmezden geliyorum. Basit bir örnek görmeyelim.)

Öte yandan, kod şu şekilde okumuşsa:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

Etiket 10'a ulaşmanın yollarının çokluğu , o noktada ave arasındaki ilişkilerden emin olmak için çok daha fazla çalışmamız gerektiği anlamına gelir b. (Aslında, genel durumda kararsızdır!)

Nokta 4 ile ilgili olarak, koddaki "bir yere gitme" kavramı sadece bir mecazdır. Elektronlar ve fotonlar (atık ısı için) dışında hiçbir şey CPU içinde hiçbir yere "gitmez". Bazen başka, daha kullanışlı bir metafordan vazgeçeriz. (Birkaç on yıl önce!) Bir dille karşılaştığımı hatırlıyorum.

if (some condition) {
  action-1
} else {
  action-2
}

action-1 ve action-2'yi hat dışı parametresiz rutinler olarak derleyerek sanal bir makineye uygulandıktan sonra, bir veya diğerini çağırmak için koşulun boole değerini kullanan tek bir iki bağımsız değişkenli VM op kodu kullanılarak uygulandı. Konsept, "buraya git ya da oraya git" yerine "şimdi ne çağırmak gerektiğini seç" idi. Yine, sadece bir metafor değişikliği.


2
İyi bir nokta. Üst düzey dillerde goto gerçekten hiçbir şey ifade etmez (Java'daki yöntemler arasında atlamayı düşünün). Haskell işlevi tek bir ifadeden oluşabilir; bir goto ile atlamayı deneyin!
Mekanik salyangoz

2
Postscript 4. nokta örneğiniz gibi çalışır.
luser droog

Smalltalk, "benzer şekilde" ile "prosedürel diller gibi bir şey yapmazsanız" anlamına gelirse 4. nokta örneğine benzer şekilde çalışır. : P Dilde akış kontrolü yok; tüm kararlar polimorfizm yoluyla ele alınır ( trueve falsefarklı tiptedir) ve bir if / else öğesinin her dalı temelde bir lambdadır.
cHao

Bunlar geçerli noktalar, ancak sonunda sadece "yanlış kullanıldığında" ne kadar kötü olabileceğini yineliyorlar. Mola, devam, çıkış, geri dönüş, gosub, settimeout, global, dahil, vb. Şeylerin akışını zihinsel olarak izlemeyi gerektiren ve hepsi de spagetti kodu oluşturmak için yanlış kullanılabilen ve değişken durumların belirsizliğini yaratmak için atlanabilir. Adil olmak gerekirse, asla ilk elden kötü bir goto kullanımı görmemiş olsam da, sadece bir veya iki kez kullanıldığını gördüm. Bu, her zaman kullanılacak daha iyi şeylerin olduğu ifadesine değinmektedir.
Beejor

gotomodern bir programlama dilinde (Go) stackoverflow.com/a/11065563/3309046 .
nishanths

245

XKCD'nin GOTO Çizgi Romanı

Bir iş arkadaşım, bir GOTO kullanmanın tek nedeninin, kendinizi bir köşeye programlamanızın tek çıkış yolu olduğunu söyledi. Başka bir deyişle, önceden uygun tasarım ve daha sonra bir GOTO kullanmanız gerekmeyecek.

Bu çizgi romanın "programın akışını yeniden yapılandırabileceğimi ya da onun yerine küçük bir" GOTO "kullanabileceğimi" gösterdiğini düşündüm. GOTO, zayıf tasarımınız olduğunda zayıf bir çıkış yoludur. Velociraptors zayıfları avlar .


41
GOTO, keyfi bir noktadan başka bir rastgele noktaya 'atlama' yapabilir. Velociraptor hiçbir yerden buraya atladı!
rpattabi

29
i şaka hiç komik bulmuyorum çünkü herkes yürütmeden önce bağlantı kurmak zorunda olduğunu biliyor.
Kinjal Dixit

31
İş arkadaşınız yanlış ve belli ki Knuth'un belgesini okumamıştı.
Jim Balter

27
Ben sadece bir goto önlemek için koymak yıllar boyunca gizli ve başka türlü bükülmüş kod sonu görmedim. @jimmcKeeth, Yukarıdaki xkcd çizgi film, goto'nun zayıf olduğunu belirlemiyor. Kullanırken histeri ile dalga geçiyor.

4
Delphi kitaplığı kaynak kodunun goto ifadeleri içerdiğinin farkındasınız. Ve bunlar goto'nun uygun kullanımlarıdır.
David Heffernan

131

Bazen GOTO'yu tek bir işlev içinde istisna işlemeye alternatif olarak kullanmak geçerlidir:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

COM kodu oldukça sık bu örüntüye düşüyor gibi görünüyor.


29
Kabul ediyorum, kodun basitleştirilebileceği ve daha okunabilir / sürdürülebilir hale getirilebileceği meşru kullanım durumları var, ancak etrafta yüzen bir çeşit goto-fobi var gibi görünüyor ...
Pop Catalin

48
@Bob: Yerel değişkenleri temizliyorsa, err_cleanup kodunu bir alt rutine taşımak zordur.
Niki

4
Ben sırf Aslında, COM / VB6 kullanmış hayır o alternatif değildi, çünkü alternatif. Bugünlerde try / catch / nihayet ne kadar mutluyum.
Rui Craveiro

10
@ user4891 Deyimsel C ++ yolu {} catch () {cleanup; }, daha ziyade, temizlenmesi gereken kaynakların yıkıcılarda yapıldığı RAII. Her kurucu / yıkıcı tam olarak bir kaynağı yönetir.
David Stone

5
Bunu c'ye gitmeden yazmanın iki yolu vardır; ve ikisi de çok daha kısadır. İkisinden biri: if (f ()) if (g ()) if (h ()) eğer başarı döndürürse; Temizlemek(); dönüş hatası; veya: (f () && g () && h ()) başarılı olursa; Temizlemek(); dönüş hatası;
LHP

124

Sadece bir kez goto kullanarak hatırlayabilirim. İç içe beş sayılan döngü dizim vardı ve belirli koşullara dayanarak tüm yapıyı içeriden erken parçalayabilmem gerekiyordu:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

Ben kolayca bir boolean break değişkeni bildirebilir ve her döngü için koşullu bir parçası olarak kullanabilirsiniz, ama bu durumda bir GOTO kadar pratik ve okunabilir olduğu karar verdim.

Hiçbir velociraptor bana saldırmadı.


83
"Bir işlev içine Refactor ve dönüş ile goto değiştirin :)", ve fark? Gerçekten fark nedir? dönüş de gitmek değil mi? İadeler, aynı zamanda, goto'nun yapılandırılmış akışını da frenler ve bu durumda aynı şekilde yaparlar (goto, ortalama şeyler için kullanılabilse bile)
Pop Catalin

38
Birçok döngüyü iç içe yerleştirmek genellikle kendi sahip olduğu bir kod kokusudur. 5 boyutlu dizi çarpma işlemi yapmadığınız sürece, iç döngülerin bazılarının daha küçük fonksiyonlara çıkarılamadığı bir durumu resmetmek zordur. Tüm kurallar gibi, sanırım birkaç istisna var.
Doug McClean

5
Bir dönüşle değiştirmek, yalnızca dönüşleri destekleyen bir dil kullanıyorsanız işe yarar.
Loren Pechtel

31
@ leppie: gotoBize karşı isyan eden ve yapılandırılmış programlama sağlayan nesil de aynı sebepten dolayı erken getirileri reddetti. Kodun ne kadar okunabilir olduğu, programcının amacını ne kadar açık bir şekilde ifade ettiği anlaşılıyor. Kötü huylu bir anahtar kelime kullanmaktan kaçınmaktan başka bir amaç için bir işlev oluşturmak kötü bir uyumla sonuçlanır: tedavi hastalıktan daha kötüdür.
Çamur

21
@ButtleButkus: Açıkçası, daha da kötüsü olmasa da bu kadar kötü. Bir ile En azından goto, bir yapabilirsiniz açıkça hedef belirtin. İle break 5;, (1) hedef bulmak için döngü kapanışları saymak zorunda; ve (2) döngü yapısı hiç değişmezse, hedefi doğru tutmak için bu sayının değiştirilmesini gerektirebilir. Eğer kaçınacaksam goto, kazanç manuel olarak böyle şeyler izlemek zorunda olmamalıdır.
cHao

93

Zaten bu tartışmayı yaptık ve ben de benim açımdan duruyorum .

Ayrıca, ben “olarak daha üst düzey dil yapılarını açıklayan insanlarla bıktım gotoonlar açıkça noktası yok çünkü kılık değiştirmiş” hiç . Örneğin:

Şemadaki gelişmiş devam kontrol yapısı bile karmaşık bir goto olarak tanımlanabilir.

Bu tam bir saçmalık. Her kontrol yapısı açısından uygulanabilir, gotoancak bu gözlem tamamen önemsiz ve işe yaramaz. gotoolumlu etkileri nedeniyle zararlı olarak kabul edilmez, ancak olumsuz sonuçları nedeniyle ve bunlar yapılandırılmış programlama ile ortadan kaldırılır.

Benzer şekilde, “GOTO bir araçtır ve tüm araçlar gibi kullanılabilir ve kötüye kullanılabilir” ifadesi tamamen işaretin dışındadır. Hiçbir modern inşaat işçisi bir kaya kullanmaz ve “bir araç” olduğunu iddia etmez. Kayaların yerini çekiçler almıştır. gotoyerini kontrol yapıları aldı. İnşaat işçisi bir çekiç olmadan vahşi doğada mahsur kalırsa, elbette bunun yerine bir kaya kullanırdı. Bir programcı X özelliğine sahip olmayan bir alt programlama dili kullanmak zorundaysa, elbette gotobunun yerine kullanması gerekebilir . Ancak, uygun dil özelliği yerine başka bir yerde kullanıyorsa dili açıkça anlamadı ve yanlış kullanıyor. Gerçekten bu kadar basit.


82
Tabii ki, bir kayanın doğru kullanımı çekiç değildir. Doğru kullanımlarından biri bir taşlama taşı veya diğer aletleri keskinleştirmek içindir. Düzgün kullanıldığında, düşük kaya bile iyi bir araçtır. Sadece doğru kullanımı bulmanız gerekir. Aynı şey goto için de geçerli.
Kibbee

10
Peki Goto'nun doğru kullanımı nedir? Akla gelebilecek her durum için daha uygun başka bir araç daha vardır. Ve hatta taşlama taşı aslında hala yapılmış olsa bile, günümüzde yüksek teknoloji araçları ile değiştirilir ait kaya. Hammadde ile alet arasında büyük bir fark vardır.
Konrad Rudolph

8
@jalf: Goto kesinlikle yok C # mevcuttur. Bkz. Stackoverflow.com/questions/359436/…
Jon Skeet

51
Pek çok insanın bu gönderiyi onaylaması beni dehşete düşürdü. Gönderiniz sadece etkili gibi görünüyordu çünkü gerçekte hangi mantığı yaptığınızı hiç sorgulamadınız, böylece yanlışlığınızı fark edemediniz. Tüm yazınızı yorumlamama izin verin: "Her durumda bir goto için üstün bir araç var, bu yüzden gotos asla kullanılmamalıdır." Bu mantıklı bir iki yönlüdür ve bu nedenle tüm yayınınız aslında "Her durumda bir goto için üstün bir araç olduğunu nereden biliyorsunuz?"
Stil ile Kodlama

10
@Coding: Hayır, gönderinin özünü tamamen kaçırdınız. Yalıtılmış, tam bir argüman yerine bir karşılanma idi . Sadece “için” ana argümanındaki yanlışlığa dikkat çektim goto. Kendine karşı bir argüman sunmadığım sürece haklısın goto- niyetim yoktu - yani soru dilenmek yok.
Konrad Rudolph

91

Sadece bir uğruna bir programa dahil edilecekler listemde son derece düşük. Bu kabul edilemez olduğu anlamına gelmez.

Goto devlet makineleri için iyi olabilir. Bir döngüdeki bir anahtar deyimi (tipik önem sırasına göre): (a) gerçekte kontrol akışını temsil etmiyor, (b) çirkin, (c) dile ve derleyiciye bağlı olarak potansiyel olarak verimsiz. Böylece her durum için bir işlev yazıp "return NEXT_STATE;" hatta goto gibi görünüyor.

Kabul edilirse, devlet makinelerini anlaşılmasını kolaylaştıracak şekilde kodlamak zordur. Bununla birlikte, bu zorluğun hiçbiri goto kullanmakla ilgili değildir ve hiçbiri alternatif kontrol yapıları kullanılarak azaltılamaz. Dilinizde 'durum makinesi' yapısı yoksa. Benimki değil.

Algoritmanızın daha spesifik bir kontrol akışı (döngüler, koşullu, ), kodda açık olmalıdır. Ve güzel bir diyagram çizmelisin.

setjmp / longjmp, istisnaları veya istisna benzeri davranışları uygulamak için iyi olabilir. Evrensel olarak övülmemesine rağmen, istisnalar genellikle "geçerli" bir kontrol yapısı olarak kabul edilir.

setjmp / longjmp, doğru şekilde kullanılması daha zor olduğu için, anlaşılır bir şekilde boş verin.

Kötü kod yazmanın en az zor olduğu herhangi bir dil hiç olmamış veya olmayacaktır. - Donald Knuth.

C dışına goto ifadesi alınması hiç kolay C. In aslında iyi kod yazmak yapmazdım, oldukça C olduğu noktayı kaçıracağı sözde yüceltilmiş montajcı dili olarak hareket etme kabiliyetine sahip olduğu.

Daha sonra "zararlı sayılan işaretçiler", ardından "ördek türü zararlı kabul edilir" olacaktır. Peki, güvenli olmayan programlama yapınızı ortadan kaldırmaya geldiklerinde sizi savunmak için kim bırakılacak? Eh?


14
Şahsen, bu çek verirdim yorum. Okuyuculara işaret etmek istediğim bir şey, "devlet makineleri" ezoterik teriminin sözcüksel analizciler gibi günlük şeyleri içermesidir. Bir kerede lex çıktısını kontrol edin. Gotolarla dolu.
TED

2
Durum makinelerini iyi yapmak için bir döngü içinde (veya olay işleyicide) bir switch deyimi kullanabilirsiniz. Bir jmp veya goto kullanmak zorunda kalmadan çok sayıda devlet makinesi yaptım.
Scott Whitlock

11
+1 Devlet makinelerindeki bu oklar, diğer kontrol yapılarından daha yakın olan 'gitmeye' eşlenir. Elbette, bir döngü içinde bir anahtar kullanabilirsiniz - tıpkı diğer problemler için bir süre yerine bir grup goto kullanabileceğiniz gibi, ancak genellikle bir fikirdir; bu tartışmanın bütün mesele bu.
Edmund

2
Size son paragrafta teklif verebilir miyim?
Chris Lutz

2
Ve burada, 2013'te, "zararlılar olarak kabul edilen işaretçiler" aşamasına çoktan girdik (ve biraz geçmiş olduk).
Isiah Meadows

68

In Linux: Çekirdek Kodu goto ifadesi kullanmak Çekirdek Tuzak üzerine, Linus Torvalds ve Linux kodunda GOTOs kullanımı hakkında "yeni adam" olan bir tartışma var. Orada çok iyi noktalar var ve Linus her zamanki kibir gibi giyinmiş :)

Bazı pasajlar:

Linus: "Hayır, Niklaus Wirth'in aslında neden bahsettiğini bildiğini düşünen CS insanları tarafından beyin yıkadınız.

-

Linus: "Bence gotolar gayet iyi ve genellikle büyük miktarda girintiden daha okunabilirler."

-

Linus: "Elbette, etiketlerin açıklayıcı olamadığı Pascal gibi aptal dillerde, goto'lar kötü olabilir."


9
Bu nasıl iyi bir nokta? Başka bir şeyi olmayan bir dilde kullanımını tartışıyorlar. Montaj içinde sen programlama, tüm şube ve atlar zaman vardır git en. Ve C, "taşınabilir bir montaj dili" dir ve öyleydi. Dahası, alıntıladığınız pasajlar neden goto'nun iyi olduğunu düşündüğü hakkında hiçbir şey söylemiyor .
jalf

5
Vay. Okumak hayal kırıklığı yaratıyor. Linus Torvalds gibi büyük bir OS adamının bunu söylemekten daha iyi bileceğini düşünürdünüz. Pascal (modern Nesne sürümü değil, eski okul pascalı) 68k döneminde Mac OS Classic'in yazdığı şeydi ve zamanının en gelişmiş işletim sistemiydi.
Mason Wheeler

6
@mason Classic Mac OS'de bazı Pascal kütüphaneleri vardı (sonunda - Pascal çalışma zamanı Mac'lerin başında çok fazla bellek aldı), ancak çekirdek kodun çoğunluğu Assembler'de, özellikle grafiklerde ve UI rutinlerinde yazılmıştır.
Jim Dovey

30
Linus yalnızca (açıkça, bu tartışmadaki Rik van Riel gibi) çıkış statüsünü ele almak için tartışır ve bunu, C'nin alternatif yapılarının yerine kullanılmaları durumunda getireceği karmaşıklık temelinde yapar.
Charles Stewart

21
IMHO Linus bu konuda haklı. Amacı, C'de yazılmış, istisna işlemeye benzer bir şey uygulaması gereken çekirdek kodunun en açık ve basit bir şekilde bir goto kullanılarak yazılmasıdır. Deyim goto cleanup_and_exitElimizdeki Şimdi geriye Goto birkaç "iyi" kullanan biridir for, whileve ifbizim kontrol akışını yönetmek için. Ayrıca bakınız: programmers.stackexchange.com/a/154980
steveha

50

C dilinde, gotoyalnızca olası hataların yerini belirleme eğiliminde olan geçerli işlev kapsamında çalışır. setjmpve longjmpçok daha tehlikelidir, yerel olmayan, karmaşık ve uygulamaya bağımlıdır. Ancak pratikte, pek çok soruna neden olacak kadar belirsiz ve nadirdirler.

gotoC'deki tehlikenin büyük ölçüde abartıldığına inanıyorum . Orijinal gotoargümanların yeni başlayanlar gibi spagetti kodlarını yazacakları eski moda BASIC gibi dillerde günlerde gerçekleştiğini unutmayın :

3420 IF A > 2 THEN GOTO 1430

Burada Linus, aşağıdakilerin uygun kullanımını tanımlar goto: http://www.kernel.org/doc/Documentation/CodingStyle (bölüm 7).


7
BASIC ilk piyasaya çıktığında, atlamanın yolları olarak GOTO nnnn ve GOSUB mmmm'ye alternatif yoktu. Yapılandırılmış yapılar sonradan eklenmiştir.
Jonathan Leffler

7
Konuyu kaçırıyorsunuz ... o zaman bile spagetti yazmak zorunda kalmadınız ... GOTO'larınız her zaman disiplinli bir şekilde kullanılabilir
JoelFan

Ayrıca, setjmp/ davranışının longjmpyalnızca aynı kapsamdaki diğer yerlerden bir kapsam içindeki bir noktaya atlama aracı olarak kullanıldıklarında belirtildiğini belirtmek gerekir. Denetim setjmpyürütüldüğünde kapsamı terk ettiğinde, longjmptarafından oluşturulan yapı üzerinde herhangi bir kullanım denemesi setjmptanımlanmamış davranışa neden olur.
supercat

9
BASIC'in bazı sürümleri bunu yapmanıza izin verir GOTO A * 40 + B * 200 + 30. Bunun nasıl çok kullanışlı ve tehlikeli olduğunu görmek zor değil.
Jon Hanna

1
@Hjulle, ifadeyi hesaplar ve daha sonra bu sayı ile kod satırına gider (açık satır numaraları, daha önceki lehçelerin bir gereksinimiydi). ZX Spectrum Basic bunu kabul edecek biriydi
Jon Hanna

48

Bugün, GOTOifade hakkında büyük bir şey görmek zor çünkü "yapılandırılmış programlama" insanlar çoğunlukla tartışmayı kazandı ve bugünün dilleri önlemek için yeterli kontrol akış yapılarına sahip GOTO.

gotoModern bir C programındaki s sayısını sayın . Şimdi sayısını ekleyin break, continueve returntabloların. Dahası, kullanmak sayısını ekleyin if, else, while, switchveya case. GOTODijkstra'nın mektubunu yazdığı 1968'de FORTRAN veya BASIC dilinde yazmış olsaydınız, programınızın kaç tane olurdu.

O sırada programlama dilleri kontrol akışında yoktu. Örneğin, orijinal Dartmouth BASIC'te:

  • IFifadeleri yoktu ELSE. Eğer isterseniz, yazmak zorundaydınız:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • İfadenizin bir IFifadeye ihtiyacı olmasa bile ELSE, yine de genellikle a GOTO.

  • DO...LOOPAçıklama yoktu . Olmayan için FORdöngüler, bir açık ile döngü sona zorunda GOTOya IF...GOTObaşlangıcına arkasına.

  • Hiç yoktu SELECT CASE. Kullanmak zorundaydın ON...GOTO.

Yani, bir ile sona erdi lot of GOTOprogramınızda s. Ve GOTOs'nin tek bir altyordam içinde sınırlandırılmasına güvenemezdiniz (çünkü GOSUB...RETURNböylesi zayıf bir altyordam kavramıydı), bu yüzden bunlar herhangi bir yereGOTO gidebilirdi . Açıkçası, bu kontrol akışını takip etmeyi zorlaştırdı.

Anti- GOTOhareket buradan geliyor.


Dikkat edilmesi gereken başka bir şey, bir döngüde nadiren yürütülmesi gereken bir kod varsa, kod yazmanın tercih edilen yolunun 420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]// olmasıdır 3230 goto 430. Bu şekilde kod yazmak, ortak ana hat durumunda alınan dalları veya atlamaları önler, ancak işleri takip etmeyi zorlaştırır. Bazı kodlar örn. +/- 128 bayt ile sınırlıysa ve bazen tamamlayıcı çiftlere sahip değilse (örn. "Cjne" vardır ancak "cje" değil), montaj kodunda daldan kaçınma daha kötü olabilir.
supercat

Bir keresinde her 128 döngüde bir kesilen 8x51 için bir kod yazdım. Bu ISR'nin ortak durumunda harcanan her ekstra döngü, ana hat kodunun yürütme hızını% 1'den fazla azaltacaktır (sanırım 128'den 90'ının 90'ı ana hat için kullanılabilirdi) ve herhangi bir dallanma talimatı iki döngü alacaktı ( hem dallanma hem de düşme vakalarında). Kodun iki karşılaştırması vardı - biri genellikle eşit rapor verecekti; diğeri, eşit değil. Her iki durumda da, nadir durum kodu 128 bayttan daha uzaktı. Yani ...
Supercat

cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough// `ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: .... Çok hoş bir kodlama deseni değil, bireysel döngüler sayıldığında böyle şeyler yapar. Tabii ki, 1960'larda, özellikle modern CPU'lar genellikle garip optimizasyonlar gerektirdiğinden ve tam zamanında derleme sistemleri, mevcut olmayan CPU'lar için optimizasyon kodunu uygulayabildiğinden, bu tür optimizasyon seviyeleri bugünden daha önemliydi. söz konusu kod yazıldı.
supercat

34

Git, belirli durumlarda "gerçek" kural dışı durum işleme için bir tür stand-in sağlayabilir. Düşünmek:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

Açıkçası bu kod daha az yer kaplayacak şekilde basitleştirildi, bu yüzden ayrıntılara fazla takılmayın. Ancak goto kullanmaktan kaçınmak için kodlayıcıların saçma uzunluklara gideceği üretim kodunda çok fazla gördüğüm bir alternatif düşünün :

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Şimdi işlevsel olarak bu kod aynı şeyi yapıyor. Aslında, derleyici tarafından üretilen kod neredeyse aynıdır. Bununla birlikte, programcının Nogoto'yu (akademik azarlayanın korkunç tanrısı) yatıştırmaya olan gayretinde , bu programcı, whiledöngünün temsil ettiği altta yatan deyimi tamamen kırdı ve kodun okunabilirliği üzerinde gerçek bir sayı yaptı. Bu daha iyi değil.

Yani, hikayenin ahlaki, eğer goto kullanmaktan kaçınmak için kendinizi gerçekten aptalca bir şeye başvurursanız, o zaman yapma.


1
Burada söylediklerinize katılma eğilimim olsa da, breakifadelerin bir kontrol yapısı içinde olması, tam olarak ne yaptıklarını açıkça ortaya koyuyor. İle gotoörneğin, kod okuma kişi teoride, aslında kontrol yapısı önce olabilir etiketi, bulmak için kod üzerinden bakmak zorundadır. Birinin kesinlikle diğerinden daha iyi olduğuna dair bir yargıya varmak için eski stil C ile yeterince deneyimli değilim, ancak her iki şekilde de değiş tokuşlar var.
Vivian River

5
@DanielAllenLangdon: aslında breakler bir döngü içindedir onlar tam olarak açıkça ortaya koyuyor döngü çıkmak . "Tam olarak yaptıkları" bu değil, çünkü gerçekte hiç bir döngü yok! İçindeki hiçbir şeyin tekrarlama şansı yoktur, ancak bu sonuna kadar net değildir. Asla bir kereden fazla çalışmayan bir "döngünüz" olması , kontrol yapılarının kötüye kullanıldığı anlamına gelir. Örnekle, gotoprogramcı söyleyebilir goto error_handler;. Daha açık ve takip edilmesi daha az zor. (Ctrl + F, "error_handler:" hedefi bulmak için. Bunu "}" ile yapmayı deneyin.)
cHao

1
Bir keresinde bir hava trafik kontrol sisteminde ikinci örneğinize benzer bir kod gördüm - çünkü 'git bizim sözlüğümüzde değil'.
QED

30

Donald E. Knuth bu soruyu 1992 CSLI "Literate Programming" kitabında yanıtladı. Üzerinde s. 17 " goto İfadeleriyle Yapısal Programlama " makalesi (PDF) var. Bence makale başka kitaplarda da yayınlanmış olabilir.

Makale Dijkstra'nın önerisini ve bunun geçerli olduğu koşulları açıklamaktadır. Ancak aynı zamanda, yalnızca yapılandırılmış döngüler kullanılarak kolayca çoğaltılamayan bir dizi karşı örnek (problem ve algoritma) verir.

Makale sorunun tam bir açıklamasını, tarihçesini, örneklerini ve karşı örneklerini içermektedir.


25

Git yararlı kabul edildi.

1975'te programlamaya başladım. 1970'lerde programcılara, "goto zararlı olarak kabul edildi" ifadesi az çok modern kontrol yapılarına sahip yeni programlama dillerinin denemeye değer olduğunu söyledi. Yeni dilleri denedik. Hızla dönüştük. Asla geri gitmedik.

Asla geri gitmedik, ama eğer gençsen, o zaman ilk etapta hiç orada bulunmadın.

Şimdi, eski programlama dillerindeki bir arka plan, programcının yaşının bir göstergesi dışında çok yararlı olmayabilir. Bununla birlikte, genç programcılar bu arka plandan yoksundur, bu yüzden artık "zararlı olarak kabul edildi" sloganı tanıtıldığında hedef kitlesine iletilen mesajı anlamazlar .

Birinin anlamadığı sloganlar çok aydınlatıcı değildir. Muhtemelen bu sloganları unutmak en iyisidir. Bu sloganlar yardımcı olmaz.

Ancak bu özel slogan, "Goto zararlı olarak kabul edildi", kendi başına ölümsüz bir yaşam sürdü.

Git istismar edilemez mi? Cevap: tabii, ama ne olmuş yani? Hemen her programlama elemanı olabilir kötüye. boolÖrneğin alçakgönüllü , bazılarımızın inanmak istediğinden daha sık istismar ediliyor.

Aksine, 1990'dan beri tek, gerçek bir goto istismar örneğiyle tanıştığımı hatırlayamıyorum.

Goto ile ilgili en büyük sorun muhtemelen teknik değil sosyaldir. Çok fazla şey bilmeyen programcılar bazen, gitmeyen goto'ların kendilerini akıllı bulduklarını düşünüyorlar. Bu tür programcıları zaman zaman tatmin etmeniz gerekebilir. Hayat böyle.

Bugün goto ile ilgili en kötü şey, yeterince kullanılmamasıdır.


24

Jay Ballou bir cevap ekleyerek çektiğimde £ 0.02 ekleyeceğim. Bruno Ranschaert daha önce yapmadıysa, Knuth'un "GOTO İfadeleriyle Yapısal Programlama" makalesinden bahsetmiştim.

Görülmediğim bir şey, tam olarak yaygın olmasa da, Fortran ders kitaplarında öğretilen bir tür kod. Genişletilmiş DO döngüsü ve açık kodlu altyordamlar gibi şeyler (unutmayın, bu Fortran II veya Fortran IV veya Fortran 66 - Fortran 77 veya 90 değil). Sözdizimsel detayların kesin olmaması ihtimali en azdır, ancak kavramlar yeterince doğru olmalıdır. Her durumda parçacıklar tek bir işlevin içindedir.

Kernighan & Plauger'ın mükemmel ama tarihli (ve baskısı tükenmiş) ' Programlama Stilinin Unsurları, 2. Baskı ' kitabının, döneminin (70'lerin sonu) programlama kitaplarından GOTO kötüye kullanımının bazı gerçek hayat örneklerini içerdiğini unutmayın. Bununla birlikte, aşağıdaki materyal bu kitaptan değildir.

DO döngüsü için genişletilmiş aralık

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

Böyle saçmalıkların bir nedeni, eski moda iyi bir yumruk kartıydı. Etiketlerin (sıralı bir şekilde güzel olduğu için kanonik stil olduğu için) sütun 1'de (aslında, 1-5 sütunlarında olması gerekiyordu) ve kodun 7-72 ​​sütunlarında olduğunu (sütun 6 devam ediyordu) görebilirsiniz. işaretleyici sütun). Sütunlara 73-80 bir sıra numarası verilecek ve delikli kart destelerini sıra numarası sırasına göre sıralayacak makineler vardı. Programınızı sıralı kartlarda bulundurduysanız ve bir döngünün ortasına birkaç kart (satır) eklemeniz gerekiyorsa, bu ekstra satırlardan sonra her şeyi tekrarlamanız gerekir. Bununla birlikte, bir kartı GOTO ile değiştirdiyseniz, tüm kartları yeniden ilişkilendirmekten kaçınabilirsiniz - rutinin sonunda yeni kartları yeni sıra numaralarıyla tıkladınız. 'Yeşil bilgi işlem' için ilk girişim olduğunu düşünün

Ah, aynı zamanda hile yaptığımı ve bağırmadığımı da not edebilirsiniz - Fortran IV normalde tüm büyük harflerle yazılmıştır.

Açık kodlu altyordam

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

76 ve 54 nolu etiketler arasındaki GOTO, hesaplanan goto'nun bir versiyonudur. İ değişkeni 1 değerine sahipse, listedeki (123) ilk etikete gidin; 2 değerine sahipse, ikincisine git vb. 76'dan hesaplanan gotona kadar olan parça açık kodlu altyordamdır. Bu, bir altyordam gibi yürütülen, ancak bir fonksiyonun gövdesinde yazılmış bir kod parçasıydı. (Fortran'ın ayrıca tek bir satıra yerleştirilmiş gömülü işlevler olan ifade işlevleri de vardı.)

Hesaplanan goto'dan daha kötü yapılar vardı - değişkenlere etiketler atayabilir ve sonra atanmış bir goto kullanabilirsiniz. Google'a atanan goto bana Fortran 95'ten silindiğini söylüyor. Dijkstra'nın "GOTO Kabul Edilen Zararlı" mektubu veya makalesi ile halka açık bir şekilde başladığı söylenebilir yapılandırılmış programlama devrimi için bir tane tebeşirleyin.

Fortran'da (ve çoğu doğru yoldan düşmüş olan diğer dillerde) yapılan şeyler hakkında biraz bilgi sahibi olmadan, yeni gelenler için Dijkstra'nın uğraştığı sorunun kapsamını anlamak zor. Heck, bu mektubun yayınlanmasından on yıl sonrasına kadar programlamaya başlamamıştım (ancak Fortran IV'te bir süre programlamak için talihsizlik yaşadım).


2
goto'Wild'da' kullanılan bazı kodların bir örneğini görmek istiyorsanız , Aranıyor: Bose-Hibbard Sıralama Algoritması sorusu , 1963'te yayınlanan bazı (Algol 60) kodlarını gösterir. Orijinal düzen, modern kodlama standartlarıyla karşılaştırılmaz. Açık (girintili) kod hala oldukça anlaşılmaz. gotoOrada ifadeleri do it (çok) zor algoritma kadar ne olduğunu anlamaya olun.
Jonathan Leffler

1
Zımba kartlarına yakın bir şey yaşamak için çok genç olmak, yeniden zımbalama sorununu okumak için aydınlatıcıydı. +1
Qix - MONICA

20

GOTO'nun zararlı olduğu düşünülen bir şey yoktur .

GOTO bir araçtır ve tüm araçlar gibi kullanılabilir ve kötüye kullanılabilir .

Orada bir eğilim lazım programlama dünyasında birçok araç, ancak, vardır istismar olmaktan çok daha kullanılır ve GOTO bunlardan biridir. İLE Delphi ifadesi başka bir şeydir.

Şahsen ben de kullanmayın tipik kodunda , ama ikisi de garip kullanımını yaşadım GOTO ve İLE garanti edildi ve alternatif çözüm daha fazla kod içerdiği olurdu.

En iyi çözüm, derleyicinin sadece anahtar kelimenin lekeli olduğu konusunda sizi uyarması ve uyarılardan kurtulmak için ifadenin etrafına birkaç pragma yönergesi doldurmanız gerekir.

Çocuklarınıza makasla koşmamalarını söylemek gibi . Makas kötü değildir, ancak bazı kullanımları sağlığınızı korumanın belki de en iyi yolu değildir.


GOTO ile ilgili sorun, modern programlama dilleri için aldığımız önemli değişmezleri kırmasıdır. Bir örnek almak gerekirse, bir işlevi çağırırsam, işlev tamamlandığında, denetimi normalde veya olağanüstü yığın çözme yoluyla arayana döndüreceğini varsayarız. Bu işlev GOTO'yu yanlış kullanırsa, elbette bu artık doğru değildir. Bu, kodumuz hakkında akıl yürütmeyi çok zorlaştırır. GOTO'nun yanlış kullanımından dikkatli bir şekilde kaçınmak yeterli değildir. GOTO bağımlılıklarımız tarafından kötüye kullanılıyorsa sorun hala devam ediyor ...
Jonathan Hartley

... Bu nedenle, işlev çağrılarımız hakkında akıl yürütme yapıp yapamayacağımızı bilmek için, geçişli bağımlılıklarımızın her birinin kaynak kodunun her satırını inceleyerek, GOTO'yu kötüye kullanmadıklarını kontrol etmeliyiz. Bu nedenle, sadece GOTO'nun dilde varlığı, kodumuzu kendimiz mükemmel kullansak (veya asla kullanmasak bile) kendi kodumuz hakkında güvenle mantık yürütme yeteneğimizi kırmıştır. Bu nedenle, GOTO sadece dikkatle kullanılacak bir araç değildir. Sistematik olarak bozulur ve bir dilde varlığı tek taraflı olarak zararlı kabul edilir.
Jonathan Hartley

gotoAnahtar kelime C # 'dan temizlenmiş olsa bile , IL'de "buraya atla" kavramı hala mevcuttur. Goto anahtar sözcüğü olmadan sonsuz bir döngü kolayca oluşturulabilir. Aranan kodun döneceğine dair bu garanti eksikliği, sizce, "kod hakkında akıl yürütme" anlamına gelir, o zaman bu yeteneğe sahip olmadığımızı söyleyebilirim.
Lasse V. Karlsen

Ah, yanlış iletişimimizin kaynağını içgörüsel olarak buldunuz. gotoC # kelimenin orijinal anlamda gerçek bir "git" değildir. Sadece bir fonksiyonun içinde atlamaya izin veren çok daha zayıf bir versiyon. "Git" i kullandığım anlam, işlemin herhangi bir yerine atlayabiliyor. Yani C # bir "goto" anahtar kelimesi olmasına rağmen, tartışmasız hiç bir zaman, gerçek bir goto yoktu. Evet, IL seviyesinde, herhangi bir dil derlemeye derlendiğinde olduğu gibi gerçek bir goto kullanılabilir. Ancak programcı bundan korunur, normal şartlar altında kullanılamaz, bu yüzden sayılmaz.
Jonathan Hartley

Bu arada, burada tarif ettiğim fikir aslında benim değil, ama 1967'den beri Dijkstra'nın orijinal makalesinin çekirdeği, 50 yıldır böyle bir alıntı yapılan meme haline gelen editörü "Goto zararlı olarak kabul edildi" çünkü bu çok devrimci, anlayışlı ve evrensel olarak kabul edilmişti.
Jonathan Hartley

17

Kendiniz için düşünebildiğiniz sürece asla olmadı.


17

Linux çekirdeğinde birkaç şey yapmaya başladığımdan beri, gotos bir zamanlar olduğu kadar beni rahatsız etmiyor. İlk başta onlar (çekirdek çocuklar) benim kod içine gotos ekledi görmek dehşete oldu. O zamandan beri bazı sınırlı bağlamlarda gotoların kullanımına alışkınım ve şimdi onları kendim kullanacağım. Tipik olarak, aynı temizleme ve kurtarma işlevinin çeşitli yerlerinde çoğaltmak yerine, bir çeşit temizlik ve kefalet yapmak için bir işlevin sonuna atlayan bir goto. Ve tipik olarak, başka bir işleve devredilecek kadar büyük bir şey değildir - örneğin, yerel olarak (k) malloc'lanmış bazı değişkenleri serbest bırakmak tipik bir durumdur.

Ben sadece bir kez setjmp / longjmp kullanılan kod yazdım. Bir MIDI davul sıralayıcı programındaydı. Oynatma, tüm kullanıcı etkileşimlerinden ayrı bir işlemde gerçekleşti ve oynatma işlemi, oynatma için gereken sınırlı bilgiyi almak için UI işlemiyle paylaşılan bellek kullandı. Kullanıcı oynatmayı durdurmak istediğinde, oynatma işlemi, kullanıcının durmasını istediği yerde yürütüldüğü her yerde karmaşık bir gevşemeden ziyade, baştan başlamak için "başlangıca geri" bir longjmp yaptı. Harika çalıştı, basitti ve bu durumda onunla ilgili herhangi bir sorun veya hata yaşamadım.

setjmp / longjmp yerlerine sahiptir - ama burası büyük olasılıkla ziyaret edemeyeceğiniz bir yer ama çok uzun bir süre.

Düzenleme: Ben sadece koda baktım. Aslında kullandığım siglongjmp () idi, longjmp değil (bu büyük bir anlaşma değil, ama siglongjmp'ın var olduğunu bile unutmuştum.)


15

C'de bir VM yazıyorsanız, (gcc) hesaplanmış goto'ları şu şekilde kullanarak ortaya çıkıyor:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

bir döngü içindeki geleneksel anahtardan çok daha hızlı çalışır.


Tek sorun, standart değil, değil mi?
Calmarius

&&op_inckesinlikle derlenmez, çünkü (solda) bir değer &bekler, fakat (sağda) bir değer &verir.
fredoverflow

7
@FredO: Özel bir GCC operatörü. Ancak, bu kodu koşulların dışındaki tüm reddederim, çünkü wtf'nin devam ettiğini kesinlikle anlayamıyorum.
Köpek yavrusu

Bu, hem goto'ların hem de şifreli kodun kullanımını doğrulayan bir örnek (hız için maksimum optimizasyon) olsa da, optimizasyon ihtiyacını, kabaca nasıl çalıştığını ve en iyi satır satır açıklamak için hala yorumlanmalıdır. yorum yapılabilir. Bu yüzden hala başarısız oluyor, çünkü bakım programcılarını karanlıkta bırakıyor. Ama VM örneğini seviyorum. Teşekkürler.
MicroservicesOnDDD

14

Çünkü gotometa programlamayı karıştırmak için kullanılabilir

GotoBir hem üst düzey ve alt düzey kontrol ifadesi, ve sonuç olarak sadece çok problem için uygun bir uygun tasarım desenlerini yoktur.

Bu var düşük seviyeli bir git ilkel operasyon olduğunu anlamda daha yüksek gibi uygular şey whileya foreachfalan.

It üst düzey belirli şekillerde kullanıldığında, açık bir sırayla yürütür, kesintisiz bir biçimde yapılandırılmış döngüler için hariç bu kodu alır anlamda ve yeterli olan, olan mantık parçalar halinde değiştirir gotos, bir kepçe - dinamik olarak yeniden bir araya getirilmiş bir mantık torbası.

Yani, prosaik ve kötü bir tarafı var goto.

Yavan tarafı ucu yukarıya doğru git mükemmel makul döngü uygulayabilir ve aşağıyı gösteren git derece mantıklı yapabilmesidir breakya return. Tabii ki, fakir insanın büyük resmi elde etmek için etkisini simüle etmek zorunda kalmayacağı için , gerçek while, breakya returnda çok daha okunabilir olurdu goto. Yani, genel olarak kötü bir fikir.

Kötülük tarafı rutin süre, aradan veya iade için goto ifadesi kullanılarak, fakat denen kullanmadığınızda içerir spagetti mantık . Bu durumda, goto-mutlu geliştirici bir goto labirentinden kod parçaları inşa ediyor ve bunu anlamanın tek yolu, bir bütün olarak zihinsel olarak simüle etmektir, birçok goto olduğunda çok yorucu bir görevdir. Demek istediğim, iç içe olanların dış tarafından reddedilen bazı şeylere izin verebileceği yerlerin elsetam tersi olmadığı yerlerde kod değerlendirme sorununu hayal edin .ififif

Son olarak, konuyu gerçekten ele almak için, Algol dışındaki tüm erken dillerin başlangıçta yalnızca sürümlerine tabi olan tek ifadeler yaptığını not etmeliyiz if-then-else. Bu nedenle, bir koşullu blok yapmanın tek yolu, gototers bir koşul kullanarak onun etrafında dolaşmaktı. Deli, biliyorum, ama bazı eski özellikleri okudum. İlk bilgisayarların ikili makine kodunda programlandığını unutmayın, bu yüzden her türlü HLL'nin bir cankurtaran olduğunu varsayalım; Sanırım tam olarak ne HLL özellikleri var hakkında çok seçici değildi.

gotoHer programa bir tane yapıştırdığımı söyledikten sonra "sadece safları rahatsız etmek için" yazdım .


4
Püresi rahatsız etmek için +1! :-)
Lumi

10

GOTO ifadesinin programcılara kullanımını reddetmek, bir marangoza çekiç kullanmamalarını söylemek gibidir, çünkü bir çivi çakarken duvara zarar verebilir. Gerçek bir programcı bir GOTO'nun nasıl ve ne zaman kullanılacağını bilir. Bu sözde 'Yapısal Programların' bazılarını izledim, sadece bir GOTO kullanmaktan kaçınmak için böyle bir Horrid kodu gördüm, programcıyı vurabilirim. Tamam, diğer tarafın savunmasında, ben de bazı gerçek spagetti kodu gördüm, o programcılar da vurulmalıdır.

İşte bulduğum kodun sadece küçük bir örneği.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------VEYA----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

3
CRT 'DOĞRU MU? (E / H): ': YORN'UN YORNA KADAR GİRİŞ =' Y 'VEYA YORN =' N '; vb
joel.neely

5
Gerçekten de, ama daha önemlisi, gerçek bir programcı bilir ne zaman değil bir kullanımı goto- ve bilir neden . Bir tabu dil yapısından kaçınmak, çünkü $ programming_guru bunu söyledi, kargo kült programlamanın tanımı budur.
Piskvor

1
İyi bir benzetme. Substrata zarar vermeden çivi sürmek için kırıcıyı ortadan kaldırmazsınız. Aksine, tırnak zımbası olarak bilinen basit bir araç kullanırsınız. Bu, tırnak kafasını güvenli bir şekilde birleştirmek için ucunda içi boş bir girintiye sahip konik bir uca sahip metalik bir pimdir (bu aletler farklı tırnaklar için farklı boyutlarda gelir). Tırnak zımbasının diğer künt ucu bir çekiçle vurulur.
Kaz


7

Orijinal makale "Koşulsuz GOTO Zararlı Olarak Değerlendirildi" olarak düşünülmelidir. Özellikle erken kodda ortak olan test-ve-atlamadan ziyade koşullu ( if) ve yinelemeli ( while) yapılara dayalı bir programlama biçimini savunuyordu . gotouygun kontrol yapısının olmadığı bazı dillerde veya durumlarda hala yararlıdır.


6

Sadece yer hakkında ben Goto hemfikir olabilir Hatalarla başa gerektiğinde kullanılacak ve bir hata oluştuğunda her bir nokta özel işlem gerektirir.

Örneğin, kaynakları alıyorsanız ve semafor veya muteks kullanıyorsanız, bunları sırayla almanız gerekir ve bunları her zaman tersi şekilde bırakmalısınız.

Bazı kodlar, bu kaynakları kapmak için çok garip bir model gerektirir ve kilitlenmeyi önlemek için bu kaynakların hem yakalanmasını hem de serbest bırakılmasını doğru bir şekilde ele almak için kolayca bakımı yapılan ve anlaşılan bir kontrol yapısı yazamazsınız.

Gitmeden doğru yapmak her zaman mümkündür, ancak bu durumda ve birkaç başka Goto aslında okunabilirlik ve sürdürülebilirlik için daha iyi bir çözümdür.

-Adam


5

Modern bir GOTO kullanımı, C # derleyicisi tarafından verim getirisi ile tanımlanan numaralandırıcılar için durum makineleri oluşturmaktır.

GOTO, programcılar tarafından değil, derleyiciler tarafından kullanılması gereken bir şeydir.


5
Sizce derleyicileri kimler yaratıyor?
tloach

10
Derleyiciler, elbette!
Seiti

3
"GOTO sadece bir derleyici tarafından yayınlanan kod tarafından kullanılması gereken bir şey" anlamına geldiğini düşünüyorum.
Simon Buchan

Bu bir dava goto. Kullandığımız olabilir Nerede gotoenumerator uygulamak için elle kodlanmış bir devlet makinesinde, sadece kullanabilirsiniz yieldyerine.
Jon Hanna

goto deyimi ile geçiş yapmak için varsayılan vaka derlemeleri ile birçok dizeyi (if-else derlemesini önlemek için) açın.
M.kazem Akhgary

5

C ve C ++ (diğer suçlular arasında) molaları belirleyip devam edene kadar, goto'nun bir rolü olmaya devam edecektir.


Yani etiketli mola ya da devam et nasıl yapmalı?
Matthew

3
Kontrol akışında tamamen keyfi sıçramalara izin vermezler.
DrPizza

5

GOTO'nun kendisi kötüyse, derleyiciler kötülük olurdu, çünkü JMP'ler üretirler. Bir kod bloğuna atlamak, özellikle bir işaretçiyi takip etmek, doğası gereği kötüyse, RETurn talimatı kötüydü. Aksine, kötülük kötüye kullanma potansiyeli içindedir.

Bazen her nesnenin olaylara yanıt olarak karmaşık bir durum dizisini takip etmesi gereken bir dizi nesneyi takip etmek zorunda kalan uygulamalar yazmak zorunda kaldım, ancak her şey kesinlikle tek iş parçacıklıydı. Sahte kodda temsil edilirse tipik bir durum dizisi şöyle olur:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Eminim bu yeni değil, ama ben C (++) ele nasıl bazı makrolar tanımlamak oldu:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Ardından (durumun başlangıçta 0 olduğu varsayılarak) yukarıdaki yapılandırılmış durum makinesi yapılandırılmış koda dönüşür:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

Bunun bir varyasyonu ile CALL ve RETURN olabilir, bu nedenle bazı durum makineleri diğer durum makinelerinin alt rutinleri gibi davranabilir.

Alışılmadık mı? Evet. Bakımcının öğrenmesi biraz zaman alıyor mu? Evet. Bu öğrenme işe yarar mı? Sanırım. Bloklara atlayan GOTO'lar olmadan yapılabilir mi? Hayır!


1
Korkudan bir dil özelliğinden kaçınmak beyin hasarının bir işaretidir. Bu da cehennemden daha şık.
Vektör Gorgoth

4

Bir iş arkadaşınızın / yöneticinin şüphesiz kullanımını bir kod incelemesinde veya ne zaman tökezlediğini sorgulayacağı için bundan kaçınırım. Kullanımları olduğunu düşünürken (örneğin hata işleme durumu) - onunla bir tür sorun yaşayabilecek diğer geliştiricilerin bir çalışmasını çalıştıracaksınız.

Buna değmez.


Try ... hakkında güzel bir şey C # blokları yakalamak bir istisna işleyici kadar kabarcıklar istisna olarak yığını ve diğer tahsis edilmiş kaynakları (yığını çözme denir) temizleme dikkat etmektir. Bu bir Try ... Catch'u Goto'dan çok daha iyi yapar, bu yüzden Try ... Catch, onu kullanın.
Scott Whitlock

Daha iyi bir çözüm var: 'do {...} süre (0);' döngü. Bu şekilde, try / catch döngüsünün ek yükü olmadan bir goto ile aynı şekilde atlayabilirsiniz (C # hakkında bilmiyorum, ancak C ++ 'da denemek düşük maliyetli ve catch yüksek maliyetli, bu yüzden görünüyor basit bir sıçramanın yeterli olacağı bir istisna atmak için aşırı).
Jim Dovey

10
Jim, buradaki sorun, bunun bir goto elde etmenin aptalca dolambaçlı bir yolundan başka bir şey olmamasıdır.
Stil ile Kodlama

4

Aslında kendimi bir goto kullanmak zorunda buldum, çünkü tam anlamıyla bu kodu yazmak için daha iyi (daha hızlı) bir yol düşünemedim:

Karmaşık bir nesnem vardı ve üzerinde biraz işlem yapmam gerekiyordu. Nesne bir durumdaysa, işlemin hızlı bir sürümünü yapabilirdim, aksi takdirde işlemin yavaş bir sürümünü yapmak zorunda kaldım. Mesele şu ki, bazı durumlarda, yavaş operasyonun ortasında, bunun hızlı operasyonla yapılabileceğini fark etmek mümkün oldu.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

Bu, gerçek zamanlı UI kodunun hız açısından kritik bir parçasındaydı, bu yüzden dürüstçe bir GOTO'nun burada haklı olduğunu düşünüyorum.

Hugo


GOTO olmayan yol, bazı ek yüke neden olan bir fast_calculations işlevini kullanmak olacaktır. Muhtemelen çoğu durumda farkedilemez, ama dediğin gibi bu hız açısından kritikti.
Kyle Cronin

1
Bu pek şaşırtıcı değil. Kesinlikle çılgın performans yönergelerine sahip tüm kodlar her zaman hemen hemen tüm en iyi uygulamaları kıracaktır. En iyi uygulamalar, 10 milisaniyeyi daha fazla sıkıştırmak veya 5 bayt daha fazla koç tasarrufu için değil, erişilebilirlik ve sürdürülebilirlik içindir.
Jonathon

1
@JonathonWisnoski, goto'nun meşru kullanımları, nereye gittiğimizi takip etmek için bir sıçanın değişken yuvalarıyla karışan spagetti kodunun çılgınca miktarlarını da ortadan kaldırır.
vonbrand

4

Bir goto'nun kullanılabileceği hemen hemen tüm durumlar, diğer yapıları kullanarak da aynısını yapabilirsiniz. Yine de derleyici tarafından Goto kullanılır.

Ben şahsen hiçbir zaman açıkça kullanmam, hiç gerek yok.


4

Buradaki cevapların hiçbirinden görmediğim bir şey , bir 'git' çözümünün sıklıkla bahsettiğim yapılandırılmış programlama çözümlerinden birinden daha verimli olmasıdır .

Çok sayıda iç içe döngü döngüsünü düşünün; burada bir grup if(breakVariable)bölüm yerine 'git' kullanmanın daha verimli olduğu açıktır. "Döngülerinizi bir işleve koyun ve dönüşü kullanın" çözümü genellikle tamamen mantıksızdır. Döngülerin yerel değişkenleri kullanması durumunda, şimdi bunları işlev parametreleri aracılığıyla geçirmeniz ve bundan kaynaklanan fazladan baş ağrısını potansiyel olarak ele almanız gerekir.

Şimdi sık sık kullandığım ve muhtemelen birçok dilde mevcut olmayan {} catch {} yapısından sorumlu olduğu temizleme vakasını düşünün. Aynı şeyi gerçekleştirmek için gerekli olan kontrol ve ekstra değişkenlerin sayısı, atlamayı yapmak için bir veya iki talimattan çok daha kötüdür ve yine, ek işlev çözümü hiç bir çözüm değildir. Bunun daha yönetilebilir veya daha okunabilir olduğunu söyleyemezsin.

Artık kod alanı, yığın kullanımı ve yürütme süresi birçok programcı için birçok durumda yeterli olmayabilir, ancak yalnızca 2KB kod alanı ile gömülü bir ortamdayken, açıkça tanımlanmış bir tanesini önlemek için 50 baytlık ek talimatlar 'git' sadece gülünçtür ve bu, birçok üst düzey programcının inandığı kadar nadir bir durum değildir.

'Gitmek zararlı' ifadesi, her zaman aşırı genelleme olsa bile, yapılandırılmış programlamaya geçmede çok yardımcı oldu. Bu noktada, hepimiz onu kullanmaya dikkat etmeliyiz (olması gerektiği gibi). Açıkçası iş için doğru araç olduğunda, bundan korkmamıza gerek yok.


3

Derin iç içe bir döngüden kırmak için kullanabilirsiniz, ancak çoğu zaman kodunuz derin iç içe döngüler olmadan daha temiz olacak şekilde yeniden düzenlenebilir.

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.