Apa yang sangat buruk dengan goto ketika digunakan untuk kasus-kasus yang jelas dan relevan ini?

40

Saya selalu tahu itu gotoadalah sesuatu yang buruk, terkunci di ruang bawah tanah di suatu tempat yang tidak pernah terlihat untuk selamanya tetapi saya mengalami contoh kode hari ini yang masuk akal untuk digunakan goto.

Saya memiliki IP di mana saya perlu memeriksa apakah ada dalam daftar IP dan kemudian melanjutkan dengan kode, jika tidak, melemparkan pengecualian.

<?php

$ip = '192.168.1.5';
$ips = [
    '192.168.1.3',
    '192.168.1.4',
    '192.168.1.5',
];

foreach ($ips as $i) {
    if ($ip === $i) {
        goto allowed;
    }
}

throw new Exception('Not allowed');

allowed:

...

Jika saya tidak menggunakan gotomaka saya harus menggunakan beberapa variabel seperti

$allowed = false;

foreach ($ips as $i) {
    if ($ip === $i) {
        $allowed = true;
        break;
    }
}

if (!$allowed) {
    throw new Exception('Not allowed');
}

Pertanyaan saya adalah apa yang sangat buruk dengan gotoketika digunakan untuk kasus-kasus yang jelas dan relevan seperti itu?

php_nub_qq
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft

Jawaban:

120

GOTO sendiri bukan masalah langsung, itu adalah mesin negara implisit yang cenderung diterapkan oleh orang-orang dengan itu. Dalam kasus Anda, Anda ingin kode yang memeriksa apakah alamat IP ada dalam daftar alamat yang dibolehkan

if (!contains($ips, $ip)) throw new Exception('Not allowed');

jadi kode Anda ingin memeriksa suatu kondisi. Algoritme untuk mengimplementasikan pemeriksaan ini seharusnya tidak menjadi masalah di sini, dalam ruang mental program utama Anda cek tersebut bersifat atomik. Begitulah seharusnya.

Tetapi jika Anda memasukkan kode yang melakukan pemeriksaan ke program utama Anda, Anda kehilangan itu. Anda memperkenalkan keadaan yang bisa berubah, baik secara eksplisit:

$list_contains_ip = undef;        # STATE: we don't know yet

foreach ($ips as $i) {
  if ($ip === $i) {
      $list_contains_ip = true;   # STATE: positive
      break;
  }
                                  # STATE: we still don't know yet, huh?                                                          
                                  # Well, then...
  $list_contains_ip = false;      # STATE: negative
}

if (!$list_contains_ip) {
  throw new Exception('Not allowed');
}

di mana $list_contains_ipsatu-satunya variabel status Anda, atau secara implisit:

                             # STATE: unknown
foreach ($ips as $i) {       # What are we checking here anyway?
  if ($ip === $i) {
    goto allowed;            # STATE: positive
  }
                             # STATE: unknown
}
                             # guess this means STATE: negative
throw new Exception('Not allowed');

allowed:                     # Guess we jumped over the trap door

Seperti yang Anda lihat, ada variabel status yang tidak dideklarasikan dalam konstruk GOTO. Itu bukan masalah semata, tetapi variabel-variabel keadaan ini seperti kerikil: membawa satu tidak sulit, membawa tas yang penuh dengannya akan membuat Anda berkeringat. Kode Anda tidak akan tetap sama: bulan depan Anda akan diminta untuk membedakan antara alamat pribadi dan publik. Bulan setelah itu, kode Anda harus mendukung rentang IP. Tahun depan, seseorang akan meminta Anda untuk mendukung alamat IPv6. Dalam waktu singkat, kode Anda akan terlihat seperti ini:

if ($ip =~ /:/) goto IP_V6;
if ($ip =~ /\//) goto IP_RANGE;
if ($ip =~ /^10\./) goto IP_IS_PRIVATE;

foreach ($ips as $i) { ... }

IP_IS_PRIVATE:
   foreach ($ip_priv as $i) { ... }

IP_V6:
   foreach ($ipv6 as $i) { ... }

IP_RANGE:
   # i don't even want to know how you'd implement that

ALLOWED:
   # Wait, is this code even correct?
   # There seems to be a bug in here.

Dan siapa pun yang harus men-debug kode itu akan mengutuk Anda dan anak-anak Anda.

Dijkstra mengatakannya seperti ini:

Penggunaan pernyataan go to go yang tidak terkendali memiliki konsekuensi langsung sehingga menjadi sangat sulit untuk menemukan serangkaian koordinat yang bermakna untuk menggambarkan proses kemajuan.

Dan itu sebabnya GOTO dianggap berbahaya.

wallenborn
sumber
22
Itu $list_contains_ip = false;pernyataan tampaknya salah tempat
Bergi
1
Tas penuh kerikil itu mudah, saya punya tas yang nyaman untuk memegangnya. : p
whatsisname
13
@Bergi: Kamu benar. Itu menunjukkan betapa mudahnya mengacaukan semua ini.
wallenborn
6
@whatsisname Tas itu disebut fungsi / kelas / paket untuk menampungnya. menggunakan goto seperti menyulapnya dan membuang tasnya.
Falco
5
Saya suka kutipan dari Dijkstra, saya belum pernah mendengarnya, tapi itu poin yang bagus. Alih-alih memiliki aliran yang cukup banyak dari atas ke bawah, tiba-tiba Anda memiliki sesuatu yang dapat melompati seluruh halaman secara acak. Singkatnya cukup mengesankan.
Bill K
41

Ada beberapa kasus penggunaan yang sah untuk GOTO. Misalnya untuk penanganan kesalahan dan pembersihan di C atau untuk menerapkan beberapa bentuk mesin negara. Tetapi ini bukan salah satu dari kasus-kasus ini. Contoh kedua adalah IMHO yang lebih mudah dibaca, tetapi bahkan lebih mudah dibaca adalah mengekstrak loop ke fungsi yang terpisah dan kemudian kembali ketika Anda menemukan kecocokan. Bahkan lebih baik (dalam pseudocode, saya tidak tahu persis sintaks):

if (!in_array($ip, $ips)) throw new Exception('Not allowed');

Jadi apa yang buruk tentang GOTOitu? Pemrograman terstruktur menggunakan fungsi dan struktur kontrol untuk mengatur kode sehingga struktur sintaksis mencerminkan struktur logis. Jika sesuatu hanya dieksekusi secara kondisional, itu akan muncul di blok pernyataan bersyarat. Jika sesuatu dieksekusi dalam satu lingkaran, itu akan muncul dalam satu blok lingkaran. GOTOmemungkinkan Anda untuk menghindari struktur sintaksis dengan melompat-lompat dengan sewenang-wenang, sehingga membuat kode lebih sulit untuk diikuti.

Tentu saja jika Anda tidak memiliki pilihan lain yang Anda gunakan GOTO, tetapi jika efek yang sama dapat dicapai dengan fungsi dan struktur kontrol, itu lebih disukai.

JacquesB
sumber
5
@jameslarge it's easier to write good optimizing compilers for "structured" languages.Peduli untuk menguraikan? Sebagai contoh balasan, orang-orang Rust memperkenalkan MIR , representasi perantara dalam kompiler yang secara khusus menggantikan loop, terus, istirahat, dan semacamnya dengan gotos, karena lebih mudah untuk memeriksa dan mengoptimalkan.
8bittree
6
"jika Anda tidak punya pilihan lain, Anda menggunakan GOTO" Ini akan sangat jarang. Jujur saya tidak bisa memikirkan kasus di mana Anda tidak punya pilihan lain, di luar pembatasan benar - benar konyol mencegah refactoring kode yang ada atau omong kosong serupa.
jpmc26
8
Juga, IMO, mengemulasi a gotodengan sarang dari flag dan kondisional (seperti pada contoh kedua OP) jauh lebih mudah dibaca daripada juts menggunakan a goto.
3
@ 8bittree: Pemrograman terstruktur dimaksudkan untuk bahasa sumber, bukan bahasa target kompilator. Bahasa target untuk kompiler asli adalah instruksi CPU, yang jelas tidak "terstruktur," dalam arti bahasa pemrograman.
Robert Harvey
4
@ 8bittree Jawaban untuk pertanyaan Anda tentang MIR ada di tautan yang Anda berikan: "Tapi tidak masalah untuk memiliki konstruksi seperti itu di MIR, karena kami tahu bahwa itu hanya akan digunakan dengan cara tertentu, seperti untuk mengekspresikan loop atau break . " Dengan kata lain, karena goto tidak diperbolehkan di Rust, mereka tahu itu terstruktur dan versi menengah dengan gotos juga. Pada akhirnya, kode harus turun ke perintah mesin seperti goto. Yang saya dapatkan dari tautan Anda adalah mereka hanya menambahkan langkah tambahan dalam konversi dari bahasa tingkat tinggi ke bahasa tingkat rendah.
JimmyJames
17

Seperti yang orang lain katakan, masalahnya bukan pada gotodirinya sendiri; masalahnya adalah bagaimana orang menggunakannya goto, dan bagaimana kode itu membuat lebih sulit untuk dipahami dan dipelihara.

Asumsikan cuplikan kode berikut:

       i = 4;
label: printf( "%d\n", i );

Untuk nilai apa dicetak i? Kapan itu dicetak? Sampai Anda memperhitungkan setiap contoh goto labeldalam fungsi Anda, Anda tidak bisa tahu. Kehadiran sederhana label itu menghancurkan kemampuan Anda untuk men-debug kode dengan inspeksi sederhana. Untuk fungsi kecil dengan satu atau dua cabang, tidak banyak masalah. Untuk fungsi yang tidak kecil ...

Jauh di awal tahun 90-an kami diberi setumpuk kode C yang menggerakkan tampilan grafis 3d dan disuruh membuatnya berjalan lebih cepat. Itu hanya sekitar 5.000 baris kode, tetapi semuanya ada di main, dan penulis menggunakan sekitar 15 atau lebih gotobercabang di kedua arah. Ini adalah kode yang buruk untuk memulai, tetapi kehadiran mereka gotomembuatnya jauh lebih buruk. Butuh rekan kerja saya sekitar 2 minggu untuk memecahkan aliran kontrol. Lebih baik lagi, gotoitu menghasilkan kode yang begitu erat dengan dirinya sendiri sehingga kami tidak dapat membuat perubahan tanpa merusak sesuatu.

Kami mencoba mengkompilasi dengan optimasi level 1, dan kompiler memakan semua RAM yang tersedia, lalu semua swap yang tersedia, dan kemudian panik sistem (yang mungkin tidak ada hubungannya dengan gotos sendiri, tapi saya suka membuang anekdot di luar sana).

Pada akhirnya, kami memberi pelanggan dua opsi - mari kita menulis ulang semuanya dari awal, atau membeli perangkat keras yang lebih cepat.

Mereka membeli perangkat keras yang lebih cepat.

Aturan Bode untuk menggunakan goto:

  1. Cabang hanya maju;
  2. Jangan melewati struktur kontrol (yaitu, jangan bercabang ke dalam tubuh dari ifatau foratau whilepernyataan);
  3. Jangan gunakan gotomenggantikan struktur kontrol

Ada kasus di mana a goto adalah jawaban yang tepat, tetapi jarang terjadi (putus dari lingkaran yang bersarang adalah satu-satunya tempat saya menggunakannya).

EDIT

Memperluas pernyataan terakhir itu, inilah salah satu dari beberapa kasus penggunaan yang valid untuk goto. Asumsikan kita memiliki fungsi berikut:

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  for ( i = 0; i < N; i ++ )
  {
    arr[i] = malloc( sizeof *arr[i] * M );
    for ( j = 0; j < M; j++ )
    {
      arr[i][j] = malloc( sizeof *arr[i][j] * P );
      for ( k = 0; k < P; k++ )
        arr[i][j][k] = initial_value();
    }
  }
  return arr;
}

Sekarang, kami memiliki masalah - bagaimana jika salah satu mallocpanggilan gagal di tengah jalan? Tidak seperti peristiwa yang mungkin terjadi, kami tidak ingin mengembalikan array yang dialokasikan sebagian, kami juga tidak ingin keluar dari fungsi dengan kesalahan; kami ingin membersihkan setelah diri kita sendiri dan menghapus semua memori yang dialokasikan sebagian. Dalam bahasa yang melempar pengecualian pada alokasi yang buruk, itu cukup mudah - Anda hanya menulis penangan pengecualian untuk membebaskan apa yang sudah dialokasikan.

Di C, Anda tidak memiliki penanganan pengecualian terstruktur; Anda harus memeriksa nilai balik dari setiap mallocpanggilan dan mengambil tindakan yang sesuai.

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  if ( arr )
  {
    for ( i = 0; i < N; i ++ )
    {
      if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
        goto cleanup_1;

      for ( j = 0; j < M; j++ )
      {
        if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
          goto cleanup_2;

        for ( k = 0; k < P; k++ )
          arr[i][j][k] = initial_value();
      }
    }
  }
  goto done;

  cleanup_2:
    // We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
    while ( j-- )
      free( arr[i][j] );
    free( arr[i] );
    // fall through

  cleanup_1:
    // We failed while allocating arr[i]; free up all previously allocated arr[i][j]
    while ( i-- )
    {
      for ( j = 0; j < M; j++ )
        free( arr[i][j] );
      free( arr[i] );
    }

    free( arr );
    arr = NULL;

  done:
    return arr;
}

Bisakah kita melakukan ini tanpa menggunakan goto? Tentu saja kita bisa - itu hanya memerlukan sedikit pembukuan tambahan (dan, dalam praktiknya, itulah jalan yang akan saya ambil). Tetapi, jika Anda mencari tempat di mana menggunakan tanda gototidak langsung merupakan praktik atau desain yang buruk, ini adalah salah satu dari sedikit.

John Bode
sumber
Saya suka aturan ini. Saya masih tidak akan menggunakan gotobahkan sesuai dengan aturan-aturan ini — saya tidak akan menggunakannya sama sekali kecuali itu satu-satunya bentuk percabangan yang tersedia dalam bahasa tersebut — tetapi saya dapat melihat bahwa tidak akan terlalu banyak mimpi buruk untuk di-debug gotoJika ditulis sesuai dengan aturan ini.
Wildcard
jika Anda perlu keluar dari loop yang sangat bersarang, Anda hanya perlu membuat loop itu fungsi lain dan kembali
bunyaCloven
6
Seperti seseorang pernah meringkasnya untuk saya: "Ini bukan masalah masuk, itu masalah yang datang dari". Sebagai spesialis optimisasi perangkat lunak saya setuju bahwa aliran kontrol yang tidak terstruktur dapat melempar kompiler (dan manusia!) Untuk satu loop. Saya pernah menghabiskan berjam-jam menerjemahkan mesin negara sederhana (empat dari lima negara) dalam fungsi BLAS, yang menggunakan berbagai bentuk GOTO, termasuk Fortran's (dalam) goto yang ditugaskan dan aritmatika yang terkenal, jika saya ingat dengan benar.
njuffa
@njuffa "Sebagai spesialis pengoptimalan perangkat lunak, saya setuju bahwa aliran kontrol yang tidak terstruktur dapat membuang kompiler (dan manusia!) untuk sebuah loop." - ada detail lebih lanjut? Saya pikir hampir setiap kompiler yang mengoptimalkan saat ini menggunakan SSA sebagai IR yang berarti menggunakan kode seperti goto di bawahnya.
Maciej Piechotka
@MaciejPiechotka Saya bukan insinyur kompiler. Ya, kompiler modern semua tampaknya menggunakan SSA dalam representasi perantara mereka. Compiler lebih suka konstruksi kontrol entri-tunggal-keluar-tunggal, tidak yakin bagaimana penggunaan SSA berperan dalam hal itu? Dengan pengamatan kode mesin yang dihasilkan dan penggunaan sumber daya kompiler, dan diskusi dengan insinyur kompiler aliran kontrol tidak terstruktur mengganggu transformasi kode optimal, mungkin karena masalah "datang-dari". Penggunaan sumber daya (waktu, memori) dapat berdampak negatif pada kinerja kode jika kompiler memutuskan untuk melewatkan optimasi karena terlalu mahal.
njuffa
12

return, break, continueDan throw/ catchsemua pada dasarnya gotos - mereka semua mentransfer kontrol untuk sepotong kode dan semua bisa dilaksanakan dengan gotos - sebenarnya saya melakukannya sekali dalam proyek sekolah, seorang instruktur PASCAL mengatakan berapa banyak lebih baik Pascal adalah dari dasar karena struktur ... jadi saya harus menjadi berlawanan ...

Hal terpenting tentang Rekayasa Perangkat Lunak (Saya akan menggunakan istilah ini di atas Pengodean untuk merujuk pada situasi di mana Anda dibayar oleh seseorang untuk membuat basis kode bersama-sama dengan insinyur lain yang memerlukan perbaikan dan pemeliharaan berkelanjutan) membuat kode dapat dibaca- -mendapatkannya untuk melakukan sesuatu hampir sekunder. Kode Anda akan ditulis hanya sekali tetapi, dalam banyak kasus, orang akan menghabiskan berhari-hari dan minggu untuk meninjau kembali / mempelajari kembali, memperbaiki dan memperbaikinya - dan setiap kali mereka (atau Anda) harus mulai dari awal dan mencoba mengingat / mencari tahu kode Anda.

Sebagian besar fitur yang telah ditambahkan ke bahasa selama bertahun-tahun adalah untuk membuat perangkat lunak lebih mudah dikelola, tidak mudah untuk ditulis (meskipun beberapa bahasa mengarah ke sana - mereka sering menyebabkan masalah jangka panjang ...).

Dibandingkan dengan pernyataan kontrol aliran yang serupa, GOTO dapat dengan mudah mengikuti yang terbaik (Goto tunggal yang digunakan dalam kasus seperti yang Anda sarankan), dan mimpi buruk ketika disalahgunakan - dan sangat mudah disalahgunakan ...

Jadi setelah berurusan dengan mimpi buruk spaghetti selama beberapa tahun, kami hanya berkata "Tidak", sebagai komunitas kami tidak akan menerima ini - terlalu banyak orang mengacaukannya jika diberi sedikit waktu luang - itu benar-benar satu-satunya masalah dengan mereka. Anda bisa menggunakannya ... tetapi bahkan jika itu adalah kasus yang sempurna, orang berikutnya akan menganggap Anda seorang programmer yang mengerikan karena Anda tidak mengerti sejarah komunitas.

Banyak struktur lain telah dikembangkan hanya untuk membuat kode Anda lebih mudah dipahami: Fungsi, Objek, Pelingkupan, Enkapsulasi, Komentar (!) ... serta pola / proses yang lebih penting seperti "KERING" (mencegah duplikasi) dan "YAGNI" (Mengurangi generalisasi berlebihan / komplikasi kode) - semua benar-benar hanya mengimpor agar orang BERIKUTNYA untuk membaca kode Anda (Siapa yang mungkin akan menjadi Anda - setelah Anda lupa sebagian besar dari apa yang Anda lakukan di tempat pertama!)

Bill K
sumber
3
Saya membatalkan ini hanya untuk kalimat, "Hal yang paling penting ... adalah membuat kode dapat dibaca; membuatnya melakukan sesuatu adalah hampir sekunder." Saya akan mengatakannya sedikit berbeda; itu tidak begitu banyak membuat kode dapat dibaca sebagai membuat kode sederhana . Tapi bagaimanapun juga, itu adalah oh-begitu-benar bahwa membuatnya melakukan sesuatu adalah hal sekunder.
Wildcard
" , Dan / semua pada dasarnya gotos" Setuju, mantan hormat ajaran pemrograman terstruktur (meskipun Anda bisa berdebat tentang pengecualian), pergi-untuk pasti tidak. Sehubungan dengan pertanyaan ini, tidak banyak yang diperoleh dari komentar ini. returnbreakcontinuethrowcatch
Eric
@ eric Anda dapat memodelkan salah satu pernyataan dengan GOTO. Anda dapat memastikan bahwa semua GOTO setuju dengan prinsip pemrograman terstruktur - mereka hanya memiliki sintaks yang berbeda dan harus, agar dapat secara persis menduplikasi fungsinya, termasuk operasi lain seperti "jika". Keuntungan dari versi terstruktur adalah bahwa mereka dibatasi hanya untuk mendukung target tertentu - Anda TAHU ketika Anda melihat terus kira-kira di mana target akan berada. Alasan yang saya sebutkan adalah untuk menunjukkan bahwa itu adalah penyalahgunaan yang menjadi masalah, bukan karena itu tidak dapat digunakan dengan benar.
Bill K
7

GOTOadalah alat. Itu bisa digunakan untuk kebaikan atau kejahatan.

Di masa lalu yang buruk, dengan FORTRAN dan BASIC, itu adalah satu - satunya alat.

Ketika melihat kode dari hari-hari itu, ketika Anda melihat GOTOAnda harus mencari tahu mengapa itu ada. Ini bisa menjadi bagian dari idiom standar yang dapat Anda pahami dengan cepat ... atau bisa menjadi bagian dari struktur kontrol mimpi buruk yang seharusnya tidak pernah terjadi. Anda tidak tahu sampai Anda melihat, dan mudah untuk keliru.

Orang menginginkan sesuatu yang lebih baik, dan struktur kontrol yang lebih maju diciptakan. Ini mencakup sebagian besar kasus penggunaan, dan orang-orang yang dibakar oleh orang jahat GOTOingin sepenuhnya melarang mereka.

Ironisnya, GOTOtidak begitu buruk ketika jarang. Ketika Anda melihatnya, Anda tahu ada sesuatu yang istimewa terjadi, dan mudah untuk menemukan label yang sesuai karena itu adalah satu-satunya label di dekatnya.

Maju cepat ke hari ini. Anda adalah dosen yang mengajar pemrograman. Anda bisa mengatakan "Dalam kebanyakan kasus, Anda harus menggunakan konstruksi baru lanjutan, tetapi dalam beberapa kasus sederhana GOTOdapat lebih mudah dibaca." Siswa tidak akan mengerti itu. Mereka akan menyalahgunakan GOTOuntuk membuat kode yang tidak dapat dibaca.

Sebaliknya Anda mengatakan " GOTOburuk. GOTOJahat. GOTOGagal ujian." Siswa akan mengerti itu !

Stig Hemmer
sumber
4
Ini sebenarnya berlaku untuk semua hal yang sudah usang. Global. Nama variabel satu huruf. Kode modifikasi diri. Eval. Bahkan, saya curiga, input pengguna tanpa filter. Setelah kita mengkondisikan diri kita untuk menghindari hal-hal seperti wabah, maka kita berada dalam posisi yang tepat untuk mengevaluasi apakah penggunaan tertentu dari mereka, dengan rambu-rambu yang baik, mungkin merupakan solusi yang tepat untuk masalah tertentu. Sebelum itu, sebaiknya kita memperlakukan mereka hanya dilarang.
Dewi Morgan
4

Dengan pengecualian goto, semua konstruk aliran dalam PHP (dan sebagian besar bahasa) dicakup secara hierarkis.

Bayangkan beberapa kode diperiksa melalui mata menyipit:

a;
foo {
    b;
}
c;

Terlepas dari apa kontrol membangun fooadalah ( if, while, dll), hanya ada perintah diperbolehkan tertentu untuk a, b, dan c.

Anda dapat memiliki a- b- c, atau a- c, atau bahkan a- b- b- b- c. Tapi Anda bisa tidak pernah memiliki b- catau a- b- a- c.

... kecuali kamu punya goto.

$a = 1;
first:
echo 'a';
if ($a === 1) {
    echo 'b';
    $a = 2;
    goto first;
}
echo 'c'; 

goto(khususnya mundur goto) dapat cukup merepotkan sehingga yang terbaik adalah membiarkannya saja, dan menggunakan konstruksi aliran yang hierarkis dan tersumbat.

gotoAda tempat, tetapi sebagian besar sebagai optimasi mikro dalam bahasa tingkat rendah. IMO, tidak ada tempat yang baik untuk itu di PHP.


FYI, kode contoh dapat ditulis lebih baik daripada salah satu saran Anda.

if(!in_array($ip, $ips, true)) {
    throw new Exception('Not allowed');
}
Paul Draper
sumber
2

Dalam bahasa tingkat rendah, GOTO tidak bisa dihindari. Tetapi pada tingkat tinggi harus dihindari (dalam hal bahasa mendukungnya) karena membuat program lebih sulit dibaca .

Semuanya bermuara untuk membuat kode lebih sulit dibaca. Bahasa tingkat tinggi ditinggikan untuk membuat kode lebih mudah dibaca daripada bahasa tingkat rendah seperti, katakanlah, assembler atau C.

GOTO tidak menyebabkan pemanasan global atau menyebabkan kemiskinan di dunia ketiga. Itu hanya membuat kode lebih sulit dibaca.

Sebagian besar bahasa modern memiliki struktur kontrol yang membuat GOTO tidak perlu. Beberapa orang seperti Java bahkan tidak memilikinya.

Bahkan, istilah kode spaguetti berasal dari kode yang berbelit-belit, sulit diikuti karena struktur percabangan yang tidak terstruktur.

Tulains Córdova
sumber
6
gotodapat membuat kode lebih mudah dibaca. Ketika Anda membuat variabel tambahan dan cabang bersarang versus satu lompatan kode jauh lebih sulit untuk dibaca dan dipahami.
Matthew Whited
@MatthewWhited Mungkin jika Anda memiliki satu goto dalam metode sepuluh baris. Tetapi kebanyakan metode tidak panjang sepuluh baris dan sebagian besar waktu ketika orang menggunakan GOTO mereka tidak menggunakannya "sekali ini saja".
Tulains Córdova
2
@MatthewWhited Bahkan, istilah kode spaguetti berasal dari kode yang berbelit-belit, sulit diikuti karena struktur percabangan yang tidak terstruktur. Atau kode spaguetti lebih mudah merah sekarang? Mungkin. Vinyl kembali, mengapa tidak GOTO.
Tulains Córdova
3
@Tulains Saya harus percaya hal-hal buruk seperti itu ada karena orang-orang terus mengeluh tentang itu, tetapi sebagian besar waktu yang saya lihat gotomuncul bukan contoh kode spaghetti yang berbelit-belit, tetapi hal-hal yang cukup mudah, sering meniru pola aliran kontrol dalam bahasa yang tidak dapat memiliki sintaks untuk itu: dua contoh paling umum adalah untuk keluar dari banyak loop dan contoh di OP di mana gotodigunakan untuk mengimplementasikan for- else.
1
Kode spageti tidak terstruktur berasal dari bahasa di mana fungsi / metode tidak ada. gotojuga bagus untuk hal-hal seperti coba lagi pengecualian, kembalikan, mesin negara.
Matthew Whited
1

Tidak ada yang salah dengan gotopernyataan itu sendiri. Kesalahannya adalah dengan beberapa orang yang secara tidak tepat menggunakan pernyataan itu.

Selain apa yang dikatakan JacquesB (penanganan kesalahan dalam C), Anda menggunakan gotountuk keluar dari loop non-bersarang, sesuatu yang dapat Anda lakukan dengan menggunakan break. Dalam hal ini sebaiknya Anda gunakan break.

Tetapi jika Anda memiliki skenario loop bersarang, maka menggunakan gotoakan lebih elegan / sederhana. Sebagai contoh:

$allowed = false;

foreach ($ips as $i) {
    foreach ($ips2 as $i2) {
        if ($ip === $i && $ip === $i2) {
            $allowed = true;
            goto out;
        }
    }
}

out:

if (!$allowed) {
    throw new Exception('Not allowed');
}

Contoh di atas tidak masuk akal dalam masalah spesifik Anda, karena Anda tidak perlu loop bersarang. Tapi saya harap Anda hanya melihat bagian loop bersarang dari itu.

Bonous point: jika daftar IP Anda kecil, metode Anda baik-baik saja. Tetapi jika daftar bertambah, ketahuilah bahwa pendekatan Anda memiliki kompleksitas run-time terburuk O (n) asimptotik . Ketika daftar Anda bertambah, Anda mungkin ingin menggunakan metode berbeda yang mencapai O (log n) (seperti struktur pohon) atau O (1) (tabel hash tanpa tabrakan).

manusia gua
sumber
6
Saya tidak yakin tentang PHP, tetapi banyak bahasa akan mendukung penggunaan breakdan continuedengan angka (untuk memecah / melanjutkan banyak lapisan loop) atau label (untuk memecah / melanjutkan loop dengan label itu), salah satu dari yang umumnya harus lebih baik menggunakan gotountuk loop bersarang.
8bittree
@ 8bittree poin bagus. Saya tidak tahu bahwa istirahat PHP adalah mewah itu. Aku hanya tahu istirahat C tidak semewah itu. Istirahat Perl (mereka menyebutnya terakhir ) juga dulu mirip dengan C (yaitu tidak suka PHP) tetapi -too- menjadi mewah setelah beberapa titik. Saya kira jawaban saya menjadi semakin tidak berguna karena lebih banyak bahasa memperkenalkan versi istirahat yang mewah :)
manusia gua
3
penembusan dengan nilai kedalaman tidak akan menyelesaikan masalah tanpa variabel tambahan. Variabel dalam dirinya sendiri yang menciptakan tingkat kompleksitas ekstra.
Matthew Whited
Tentu saja, hanya karena Anda memiliki loop bersarang bukan berarti Anda perlu kebagian, ada cara lain untuk menangani situasi itu dengan elegan.
NPSF3000
@ NPSF3000 Bisakah Anda menawarkan contoh loop bersarang yang dapat Anda keluar tanpa menggunakan pernyataan goto , sementara juga menggunakan bahasa yang tidak memiliki versi break yang mewah yang menentukan kedalaman?
manusia gua
0

Dengan goto saya bisa menulis kode lebih cepat!

Benar. Tidak peduli

Goto ada di majelis! Mereka hanya menyebutnya jmp.

Benar. Tidak peduli

Goto memecahkan masalah dengan lebih sederhana.

Benar. Tidak peduli

Di tangan kode pengembang yang disiplin yang menggunakan goto bisa lebih mudah dibaca.

Benar. Namun, saya telah menjadi programmer yang disiplin. Saya telah melihat apa yang terjadi pada kode dari waktu ke waktu. Gotodimulai dengan baik. Kemudian keinginan untuk menggunakan kembali kode masuk. Cukup segera saya menemukan diri saya di breakpoint tidak memiliki petunjuk apa pun yang terjadi bahkan setelah melihat keadaan program. Gotomembuatnya sulit untuk alasan tentang kode. Kami telah bekerja menciptakan benar-benar keras while, do while, for, for each switch, subroutines, functions, dan banyak lagi semua karena melakukan hal-hal ini dengan ifdan gotosulit pada otak.

Jadi tidak. Kami tidak ingin melihatnya goto. Tentu itu hidup dan baik dalam biner tetapi kita tidak perlu melihatnya dalam sumber. Bahkan, ifmulai terlihat sedikit goyah.

candied_orange
sumber
1
"Goto memecahkan masalah lebih sederhana." / "Benar. Tidak peduli." - Saya benci melihat kode Anda di mana Anda sengaja menghindari solusi sederhana demi apa yang paling bisa dikembangkan. (Kudengar YAGNI?)
user253751
1
@Wildcard if dan goto adalah cara yang sangat sederhana untuk menyelesaikan masalah yang sebaiknya diselesaikan dengan cara lain. Mereka adalah buah yang menggantung rendah. Ini bukan tentang bahasa tertentu. Gotomemungkinkan Anda mengabstraksi masalah dengan membuatnya sedikit masalah kode lainnya. Ifmemungkinkan Anda memilih abstraksi itu. Ada cara yang lebih baik untuk abstrak dan cara yang lebih baik untuk memilih abstraksi. Polimorfisme untuk satu.
candied_orange
2
Meskipun saya setuju dengan Anda, ini adalah jawaban dengan kata-kata yang buruk. "Benar, tidak peduli" bukanlah argumen yang meyakinkan untuk apa pun. Saya pikir pesan utama Anda adalah ini: meskipun goto menarik bagi orang-orang yang lebih suka menggunakan alat yang efisien dan kuat, itu mengarah pada sejumlah besar waktu yang terbuang sia-sia di kemudian hari, bahkan ketika perawatan dilakukan.
Artelius
1
@artelius Tema "benar, tidak peduli" tidak dimaksudkan untuk meyakinkan. Ini dimaksudkan untuk mengilustrasikan banyak poin yang saya akui untuk kebagian dan masih menentangnya. Jadi memperdebatkan poin-poin itu dengan saya tidak ada gunanya. Karena itu bukan poin saya. Dapatkan intinya?
candied_orange
1
Saya hanya percaya bahwa jawaban yang baik tidak hanya menyatakan pendapat Anda, tetapi juga menjelaskannya, dan memungkinkan pembaca untuk membuat penilaian sendiri. Seperti berdiri, tidak jelas mengapa poin bagus goto tidak cukup untuk melebihi masalahnya. Sebagian besar dari kita menghabiskan waktu berjam-jam untuk mencari bug yang tidak melibatkan goto. Bukan satu-satunya konstruksi yang sulit dipikirkan. Seseorang yang mengajukan pertanyaan ini mungkin kurang berpengalaman dan membutuhkan hal-hal yang dimasukkan dalam perspektif.
Artelius
-1

Bahasa assembly biasanya hanya memiliki lompatan bersyarat / tanpa syarat (setara dengan GOTO. Implementasi FORTRAN dan BASIC yang lebih lama tidak memiliki pernyataan blok kontrol di luar iterasi yang dihitung (loop DO), meninggalkan semua aliran kontrol lainnya ke IFs dan GOTO. DO loop di bahasa-bahasa ini diakhiri dengan pernyataan berlabel numerik. Akibatnya, kode yang ditulis untuk bahasa-bahasa ini bisa, dan sering kali, sulit untuk diikuti dan rawan kesalahan.

Untuk menggarisbawahi intinya, ada pernyataan " DATANG DARI ".

Praktis tidak perlu menggunakan GOTO dalam bahasa seperti C, C ++, C #, PASCAL, Java, dll .; konstruksi alternatif dapat digunakan yang hampir pasti akan sama efisien dan jauh lebih mudah dirawat. Memang benar bahwa satu GOTO dalam file sumber tidak akan menjadi masalah. Masalahnya adalah bahwa tidak perlu banyak untuk membuat unit kode sulit untuk diikuti dan rawan kesalahan pemeliharaan. Itu sebabnya kebijaksanaan yang diterima adalah untuk menghindari GOTO jika memungkinkan.

Ini artikel wikipedia pada pernyataan goto mungkin bisa membantu

Zenilogix
sumber