Обработка StackOverflow

program StackOverflow;

function BadFactorial(k: integer): integer;
begin
   BadFactorial := k*BadFactorial(k-1);
end;

begin
   try
      Writeln(BadFactorial(5));
   except
      Writeln('Исключение');
   end;
end.

Почему не появляется надпись “Исключение”?

Код перепроверил - скорее всего проблема в самом .NET. Код на C# работает точно так же:

using System;

namespace Example
{
	class Program
	{
		public static int BadFactorial(int k){
			return BadFactorial(k - 1);
		}
		public static void Main(string[] args)
		{
			try{
				Console.WriteLine(BadFactorial(5));
			}
			catch{
				Console.WriteLine("Исключение");
			}
		}
	}
}
1 лайк

Дело в том, что StackOverflow это особое исключение. Чтоб обработать исключение - надо ещё дополнительное место на стеке, но раз его уже и так нет - то и обработать его не получается.

Читайте StackOverflowException Class:

In the .NET Framework 1.0 and 1.1, you could catch a StackOverflowException object (for example, to recover from unbounded recursion). Starting with the .NET Framework 2.0, you can’t catch a StackOverflowException object with a try/catch block, and the corresponding process is terminated by default.

. @Gleb, для BadFactorial Вы могли бы использовать epxression-body запись метода, в PascalABC.Net аналог, в некотором смысле, данной фичи - это:

function BadFactorial(k: integer): integer := k * BadFactorial(k - 1);

.

Вообще, плохой практикой считается создавать подпрограммы с неконтролируемым уровнем рекурсии.

Даже если в функции BadFactorial будет стоять if, вырубающий программу - этот код всё равно может вызвать ошибку, если передать большой k.

Кроме того, JIT не может оптимизировать этот код, и он и без того будет ужасно медленным.

Но, всегда есть способ заменить рекурсию обычным кодом. Именно всегда, это как то логически доказывается, не помню как.
К примеру:

function Factorial(k: integer): integer;
begin
  Result := 1;
  for var i := 2 to k do
    Result *= i;
end;

begin end.

Ну, это простой пример. В более сложных, к примеру, в программе которая читает и выполняет скрипты в которых может содержаться рекурсия - можно не использовать стандартную рекурсию, а вместо этого, создать переменную типа Stack<*Запись, содержащая данные о точке вызова*>. И при каждом рекурсивном вызове - добавлять координаты откуда произошёл вызов - в эту переменную. В таком случае вы сами решаете, сколько уровней рекурсии разрешено, и что делать когда их становится слишком много.

Дополню. Поэтому можно обрабатывать еще и System.OverflowException.

В смысле?

Вы сами правильно сказали, что при больших k программа может выдать ошибку. Я сказал как можно обработать её.

А я вот не понял. Как её где можно обработать, если она сразу вырубает программу.

Вы меня не поняли. Имелась ввиду обработка System.OverflowException в том случае, при отсутствии падения программы по причине System.StackOverflowException.

Приведите код наконец)) В программировании код всегда громче слов.

using System;

namespace Example
{
	class Program
	{
		public static int BadFactorial(int k)
		{
			if (k <= 0)
			{
				return 1;
			}
			else
			{
				return checked(k * BadFactorial(k - 1)); // <-- checked
			}
		}
		
		public static void Main(string[] args)
		{
			try
			{
				Console.WriteLine(BadFactorial(2000));
			}
			catch
			{
				Console.WriteLine("Ошибочка вышла...");
			}
			Console.ReadLine();
		}
	}
}

Друзья, речь, разумеется, не о факториале. Просто обнаружили, что не можем вообще обработать StackOverflowException. А пример про плохой факториал - это просто самое простое, что взято для иллюстрации.

И поэтому в PascalABC.NЕТ надо срочно притащить checked ?

А вот я обнаружил у языка (на мой взгляд, конечно) куда более существенный недостаток: он не умеет по утрам в постель кофе подавать! Как жить после этого - уме не приложу!

Это вопрос ко мне? Если да, то не по адресу. Про checked ничего не знаю и знать не хочу. А вот зачем начинаете хамить - мне непонятно.

Согласитесь, что если в языке объявлена некоторая возможность, она должна работать. Разве нет?

1 лайк

Где это Вы хамство увидели?

А она объявлена в PascalABC.NЕТ ? Можете сослаться на место в Справке?

Про кофе - откровенное хамство.

3 лайка

Ну, такое легче искать тут и там. Вот если не можете найти для C# - значит это может быть ошибка паскаля, но в данном случае по этому поводу полно инфы.

Нажмите F1 на слове try, например.

С каких пор гипербола считается хамством? Или у Вас свои собственные мерки морали?

Смысл? Это вообще не паскалевский текст в паскалевской почему-то ветке.