Інтуїтивна відповідь полягає в тому, що якщо у вас немає безмежних циклів і у вас немає рекурсії і у вас немає goto, ваші програми припиняються. Це не зовсім вірно, є й інші способи прокрадання неприпинення, але це досить добре для більшості практичних випадків. Звичайно, навпаки невірно, є мови з цими конструкціями, які не дозволяють не закінчувати програми, але вони використовують інші види обмежень, такі як системи складних типів.
Рекурсія
Поширене обмеження в мовах сценаріїв полягає в динамічному запобіганні рекурсії: якщо A дзвінки B викликає C дзвінки ... дзвінки A, то перекладач (або перевіряючий у вашому випадку) відмовляється або сигналізує про помилку, навіть якщо рекурсія може фактично закінчитися. Два конкретні приклади:
Препроцесор C залишає макрос недоторканим, коли він розширює цей макрос. Найбільш поширене використання - це визначення обгортки навколо функції:
#define f(x) (printf("calling f(%d)\n", (x)), f(x))
f(3);
Це розширюється до
(printf("calling f(%d)\n", (3)), f(3))
Також обробляється взаємна рекурсія. Наслідком цього є те, що препроцесор C завжди припиняється, хоча можна створити макроси з високою складністю виконання.
#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)
Оболонки Unix розширюють псевдоніми рекурсивно, але лише до тих пір, поки вони не зустрінуться з псевдонімом, який вже розгортається. Знову ж таки, головна мета - визначити псевдонім для аналогічно названої команди.
alias ls='ls --color'
alias ll='ls -l'
Очевидне узагальнення полягає в тому, щоб дозволити глибину рекурсії до , з можливо можливо налаштувати.нн
Існують більш загальні методи доказування припинення рекурсивних викликів, таких як пошук деякого додатного цілого числа, яке завжди зменшується від одного рекурсивного виклику до іншого, але їх виявити значно складніше. Їх часто важко перевірити, не кажучи вже про висновки.
Петлі
Цикли закінчуються, якщо ви можете зв'язати кількість ітерацій. Найпоширенішим критерієм є те, що якщо у вас є for
цикл (без хитрощів, тобто такий, який дійсно рахується від до ), він виконує обмежену кількість ітерацій. Отже, якщо тіло петлі припиняється, петля сама припиняється.мн
Зокрема, за допомогою циклів (плюс розумні конструкції мови, такі як умовні умови), ви можете записати всі примітивні рекурсивні функції , і навпаки. Ви можете розпізнати примітивні рекурсивні функції синтаксично (якщо вони написані безперешкодно), тому що вони не використовують цикл, ані гото, ані рекурсію чи інший трюк. Примітивні рекурсивні функції гарантовано припиняються, і більшість практичних завдань не виходять за рамки примітивної рекурсії.