Jawaban intuitifnya adalah bahwa jika Anda tidak memiliki loop yang tidak terikat dan Anda tidak memiliki rekursi dan Anda tidak punya kebagian, program Anda berakhir. Ini tidak sepenuhnya benar, ada cara lain untuk menyelundupkan non-terminasi, tetapi cukup baik untuk sebagian besar kasus praktis. Tentu saja kebalikannya salah, ada bahasa dengan konstruksi ini yang tidak memungkinkan program non-terminating, tetapi mereka menggunakan jenis pembatasan lain seperti sistem tipe canggih.
Pengulangan
Pembatasan umum dalam bahasa skrip adalah secara dinamis mencegah rekursi: jika A memanggil B memanggil C memanggil ... panggilan A, maka penerjemah (atau pemeriksa, dalam kasus Anda) menyerah atau memberi sinyal kesalahan, bahkan jika rekursi itu mungkin benar-benar berakhir. Dua contoh nyata:
Preprosesor C membiarkan makro tetap utuh saat memperluas makro itu. Penggunaan yang paling umum adalah mendefinisikan wrapper di sekitar fungsi:
#define f(x) (printf("calling f(%d)\n", (x)), f(x))
f(3);
Ini berkembang menjadi
(printf("calling f(%d)\n", (3)), f(3))
Rekursi timbal balik juga ditangani. Konsekuensinya adalah bahwa preprosesor C selalu berakhir, meskipun mungkin untuk membangun makro dengan kompleksitas run-time yang tinggi.
#define f0(x) x(x)x(x)
#define f1(x) f0(f0(x))
#define f2(x) f1(f1(x))
#define f3(x) f2(f2(x))
f3(x)
Kerang Unix memperluas alias secara rekursif, tetapi hanya sampai mereka menemukan alias yang sudah diperluas. Sekali lagi, tujuan utamanya adalah untuk menetapkan alias untuk perintah yang bernama sama.
alias ls='ls --color'
alias ll='ls -l'
nn
Ada teknik yang lebih umum untuk membuktikan bahwa panggilan rekursif berakhir, seperti menemukan beberapa bilangan bulat positif yang selalu berkurang dari satu panggilan rekursif ke yang berikutnya, tetapi ini jauh lebih sulit untuk dideteksi. Mereka seringkali sulit diverifikasi, apalagi disimpulkan.
Loop
for
mn
Secara khusus, dengan for loop (ditambah konstruksi bahasa yang masuk akal seperti conditional), Anda dapat menulis semua fungsi rekursif primitif , dan sebaliknya. Anda dapat mengenali fungsi rekursif primitif secara sintaksis (jika mereka ditulis dengan cara yang tidak dikobarkan), karena mereka tidak menggunakan while atau goto atau rekursi atau trik lainnya. Fungsi rekursif primitif dijamin akan berakhir, dan sebagian besar tugas praktis tidak melampaui rekursi primitif.