Saya berencana untuk mengajar kursus musim dingin tentang berbagai topik, salah satunya akan menjadi penyusun. Sekarang, saya menemukan masalah ini sambil memikirkan tugas untuk diberikan sepanjang kuartal, tapi itu membuat saya bingung sehingga saya dapat menggunakannya sebagai contoh.
public class DeadCode {
public static void main(String[] args) {
return;
System.out.println("This line won't print.");
}
}
Dalam program di atas, jelas bahwa pernyataan cetak tidak akan pernah dijalankan karena return
. Kompiler terkadang memberikan peringatan atau kesalahan tentang kode mati. Misalnya, kode di atas tidak akan dikompilasi di Jawa. Kompiler javac, bagaimanapun, tidak akan mendeteksi semua contoh kode mati di setiap program. Bagaimana saya membuktikan bahwa tidak ada kompiler yang dapat melakukannya?
BigInteger i = 0; while(isCollatzConjectureTrueFor(i)) i++; printf("Hello world\n");
Jawaban:
Itu semua berasal dari ketidakpastian masalah penghentian. Misalkan kita memiliki fungsi kode mati "sempurna", beberapa Mesin Turing M, dan beberapa string input x, dan prosedur yang terlihat seperti ini:
Jika M berjalan selamanya, maka kami menghapus pernyataan cetak, karena kami tidak akan pernah mencapainya. Jika M tidak berjalan selamanya, maka kita perlu menjaga pernyataan cetak. Jadi, jika kita memiliki penghapus kode mati, itu juga memungkinkan kita menyelesaikan Masalah Pemutusan, jadi kita tahu tidak akan ada penghapus kode mati semacam itu.
Cara kita menyiasatinya adalah dengan "perkiraan konservatif." Jadi, dalam contoh Mesin Turing saya di atas, kita dapat mengasumsikan bahwa menjalankan M pada x mungkin selesai, jadi kami memainkannya dengan aman dan tidak menghapus pernyataan cetak. Dalam contoh Anda, kami tahu bahwa apa pun fungsi yang dilakukan atau tidak dihentikan, tidak mungkin kami akan mencapai pernyataan cetak itu.
Biasanya, ini dilakukan dengan membangun "grafik aliran kendali". Kami membuat asumsi yang disederhanakan, seperti "akhir putaran sementara terhubung ke awal dan pernyataan setelah", bahkan jika itu berjalan selamanya atau hanya berjalan sekali dan tidak mengunjungi keduanya. Demikian pula, kami mengasumsikan bahwa pernyataan if dapat mencapai semua cabangnya, bahkan jika pada kenyataannya beberapa tidak pernah digunakan. Jenis penyederhanaan ini memungkinkan kami untuk menghapus "kode mati jelas" seperti contoh yang Anda berikan, namun tetap dapat diperhitungkan.
Untuk mengklarifikasi beberapa kebingungan dari komentar:
Seperti yang dikatakan Raphael, dalam contoh saya, kami menganggap Mesin Turing sebagai input. Idenya adalah bahwa, jika kita memiliki algoritma DCE yang sempurna, kita akan dapat membuat cuplikan kode yang saya berikan untuk Mesin Turing apa pun , dan memiliki DCE akan menyelesaikan masalah penghentian.
Untuk masalah njzk2 memunculkan: Anda benar sekali, dalam hal ini Anda dapat menentukan bahwa tidak ada cara pernyataan setelah pengembalian dapat dicapai. Ini karena cukup sederhana sehingga kita dapat menggambarkan ketidakterjangkauannya dengan menggunakan batasan grafik aliran-kontrol (yaitu tidak ada tepi keluar dari pernyataan kembali). Tetapi tidak ada eliminator kode mati yang sempurna, yang menghilangkan semua kode yang tidak digunakan.
Untuk TomášZato: ini bukan bukti yang bergantung pada input. Sebaliknya, tafsirkan sebagai "forall". Ini berfungsi sebagai berikut: anggap kita memiliki algoritma DCE yang sempurna. Jika Anda memberi saya Turing Machine M acak dan input x, saya dapat menggunakan algoritma DCE untuk menentukan apakah M berhenti, dengan membuat cuplikan kode di atas dan melihat apakah pernyataan cetak dihapus. Teknik ini, meninggalkan parameter sewenang-wenang untuk membuktikan pernyataan forall, adalah umum dalam matematika dan logika.
Saya tidak sepenuhnya mengerti poin TomášZato tentang kode menjadi terbatas. Tentunya kode ini terbatas, tetapi algoritma DCE yang sempurna harus berlaku untuk semua kode, yang merupakan kumpulan infinte. Demikian juga, sementara kode itu sendiri terbatas, set input potensial tidak lengkap, seperti potensi waktu berjalan dari kode.
Mengenai mempertimbangkan cabang terakhir yang tidak mati: aman dalam hal "perkiraan konservatif" yang saya bicarakan, tetapi tidak cukup untuk mendeteksi semua contoh kode mati seperti yang diminta OP.
Pertimbangkan kode seperti ini:
Jelas kami dapat menghapus
print "goodbye"
tanpa mengubah perilaku program. Jadi, itu adalah kode mati. Tetapi jika ada pemanggilan fungsi yang berbeda alih-alih(true)
dalamwhile
kondisi, maka kita tidak tahu apakah kita dapat menghapusnya atau tidak, yang mengarah pada ketidakpastian.Perhatikan bahwa saya tidak membuat ini sendirian. Ini adalah hasil yang terkenal dalam teori kompiler. Ini dibahas dalam The Tiger Book . (Anda mungkin dapat melihat di mana mereka berbicara di dalam buku Google .
sumber
Ini adalah twist pada jawaban jmite yang menghindari potensi kebingungan tentang non-terminasi. Saya akan memberikan program yang selalu berhenti sendiri, mungkin memiliki kode mati tetapi kita tidak dapat (selalu) secara algoritmik memutuskan apakah ada.
Pertimbangkan kelas input berikut untuk pengidentifikasi kode mati:
Sejak
M
danx
diperbaiki,simulateMs
memiliki kode mati denganreturn 0
jika dan hanya jikaM
tidak berhentix
.x
Karenanya, pemeriksaan kode mati tidak dapat dihitung.
Jika Anda tidak terbiasa dengan reduksi sebagai teknik bukti dalam konteks ini, saya merekomendasikan bahan referensi kami .
sumber
Cara sederhana untuk mendemonstrasikan properti semacam ini tanpa terperangkap dalam rincian adalah dengan menggunakan lemma berikut:
Lemma: Untuk setiap kompiler C untuk bahasa Turing-complete, ada fungsi
undecidable_but_true()
yang tidak mengambil argumen dan mengembalikan boolean true, sehingga C tidak dapat memprediksi apakahundecidable_but_true()
mengembalikan benar atau salah.Perhatikan bahwa fungsinya tergantung pada kompiler. Diberikan fungsi
undecidable_but_true1()
, kompiler selalu dapat ditambah dengan pengetahuan apakah fungsi ini mengembalikan benar atau salah; tetapi selalu ada beberapa fungsi lainundecidable_but_true2()
yang tidak akan dibahas.Bukti: menurut teorema Rice , properti "fungsi ini mengembalikan nilai sebenarnya" tidak dapat diputuskan. Karenanya setiap algoritma analisis statis tidak dapat memutuskan properti ini untuk semua fungsi yang mungkin.
Konsekuensi: Diberikan kompiler C, program berikut berisi kode mati yang tidak dapat dideteksi:
Catatan tentang Java: mandat bahasa Jawa bahwa kompiler menolak program tertentu yang berisi kode yang tidak dapat dijangkau, sementara mandat yang masuk akal bahwa kode disediakan di semua titik yang dapat dijangkau (misalnya aliran kontrol dalam fungsi non-void harus diakhiri dengan
return
pernyataan). Bahasa tersebut menentukan dengan tepat bagaimana analisis kode yang tidak terjangkau dilakukan; jika tidak maka tidak mungkin untuk menulis program portabel. Diberikan program formulirperlu untuk menentukan dalam kasus apa kode yang tidak dapat dijangkau harus diikuti oleh beberapa kode lain dan dalam kasus apa itu tidak boleh diikuti oleh kode apa pun. Contoh program Java yang berisi kode yang tidak dapat dijangkau, tetapi tidak dengan cara yang diizinkan oleh penyusun Java, muncul di Java 101:
sumber
day_of_week
tidak terjangkau.Jawaban jmite berlaku untuk apakah program akan pernah keluar dari perhitungan - hanya karena tidak terbatas saya tidak akan memanggil kode setelah mati.
Namun, ada pendekatan lain: Masalah yang ada jawabannya tetapi tidak diketahui:
Rutin ini tanpa diragukan lagi memang mengandung kode mati - fungsi akan mengembalikan jawaban yang mengeksekusi satu jalur tetapi tidak yang lain. Semoga berhasil menemukannya! Ingatan saya, tidak ada komputer teoretis yang dapat memecahkan masalah ini dalam umur semesta.
Lebih detail:
The
Evaluate()
Toedjoe menghitung fungsi sisi mana memenangkan permainan catur jika kedua belah pihak bermain sempurna (dengan kedalaman pencarian maksimum).Evaluator catur biasanya melihat ke depan pada setiap gerakan yang mungkin dilakukan dengan kedalaman tertentu dan kemudian mencoba untuk menilai papan pada titik itu (kadang-kadang memperluas cabang tertentu lebih jauh dengan melihat setengah melalui pertukaran atau sejenisnya dapat menghasilkan persepsi yang sangat miring.) Karena kedalaman maksimum sebenarnya adalah 17695 setengah-bergerak pencarian sudah lengkap, itu akan melintasi setiap permainan catur yang mungkin. Karena semua permainan berakhir, tidak ada masalah mencoba memutuskan seberapa baik posisi masing-masing papan (dan dengan demikian tidak ada alasan untuk melihat logika evaluasi papan - itu tidak akan pernah disebut), hasilnya adalah menang, kalah atau Gambaran. Jika hasilnya seri, permainan itu adil, jika hasilnya bukan seri, itu adalah permainan yang tidak adil. Untuk sedikit mengembangkannya kita dapatkan:
Perhatikan, juga, bahwa hampir tidak mungkin bagi kompiler untuk menyadari bahwa Chessboard.Score () adalah kode mati. Pengetahuan tentang aturan catur memungkinkan kita manusia untuk mengetahui hal ini, tetapi untuk mengetahui hal ini, Anda harus tahu bahwa MakeMove tidak pernah dapat meningkatkan jumlah keping dan bahwa Papan Catur. Gambar () akan kembali benar jika jumlah keping tetap statis terlalu lama .
Perhatikan bahwa kedalaman pencarian berada dalam setengah gerakan, bukan keseluruhan gerakan. Ini normal untuk rutin AI semacam ini karena ini merupakan rutin O (x ^ n) - menambahkan satu lapis pencarian memiliki efek besar pada berapa lama waktu yang dibutuhkan untuk menjalankan.
sumber
Saya pikir dalam kursus komputasi, gagasan kode mati menarik dalam konteks memahami perbedaan antara waktu kompilasi dan waktu berjalan!
Kompiler dapat menentukan kapan Anda memiliki kode yang tidak pernah dalam skenario kompilasi-waktu yang dapat dilalui, tetapi tidak dapat melakukannya untuk runtime. loop sementara sederhana dengan input pengguna untuk tes loop-break menunjukkan hal itu.
Jika kompiler benar-benar dapat menentukan kode mati runtime (yaitu discern Turing complete) maka ada argumen bahwa kode tidak perlu dijalankan, karena pekerjaan sudah selesai!
Jika tidak ada yang lain, keberadaan kode yang lolos dari kompilasi kode waktu mati menggambarkan perlunya memeriksa batas pragmatis pada input dan kebersihan kode umum (di dunia nyata dari proyek nyata.)
sumber