Программирование на языке С#: разработка консольных приложений

Обработка исключений

Разбить на страницы
Показывать лекцию целиком

Язык С#, как и многие другие объектно-ориентированные языки, реагирует на ошибки и ненормальные ситуации с помощью механизма обработки исключений.Исключение - это объект, генерирующий информацию о "необычном программном происшествии". При этом важно проводить различие между ошибкой в программе, ошибочной ситуацией и исключительной ситуацией.

Ошибка в программе допускается программистом при ее разработке. Например, вместо операции сравнения ( == ) используется операция присваивания ( = ). Программист должен исправить подобные ошибки до передачи кода программы заказчику. Использование механизма обработки исключений не является защитой от ошибок в программе.

Ошибочная ситуация вызвана действиями пользователя. Например, пользователь вместо числа ввел строку. Такая ошибка способна вызывать исключение. Программист должен предвидеть ошибочные ситуации и предотвращать их с помощью операторов, проверяющих допустимость поступающих данных.

Даже если программист исправил все свои ошибки в программе, предвидел все ошибочные ситуации, он все равно может столкнуться с непредсказуемыми и неотвратимыми проблемами - исключительными ситуациями. Например, нехваткой доступной памяти или попыткой открыть несуществующий файл. Исключительные ситуации программист предвидеть не может, но он может отреагировать на них так, что они не приведут к краху программы.

Для обработки ошибочных и исключительных ситуаций в С# используется специальная подсистема обработки исключений. Преимущество данной подсистемы состоит в автоматизации создания большей части кода по обработке исключений. Раньше этот код приходилось вводить в программу "вручную". Кроме этого обработчик исключений способен распознавать и выдавать информацию о таких стандартных исключениях, как деление на нуль или попадание вне диапазона определения индекса.

Оператор try

В С# исключения представляются классами. Все классы исключений порождены от встроенного класса исключений Exception, который определен в пространстве имен System.

Управление обработкой исключений основывается на использовании оператора try. Синтаксис оператора:

try	// контролируемый блок
{ 
…
}
catch //один или несколько блоков обработки исключений
{ 
…
}
finally	//блок завершения
{
…
}

Программные инструкции, которые нужно проконтролировать на предмет исключений, помещаются в блок try. Если исключение возникает в этом блоке, оно дает знать о себе выбросом определенного рода информации. Выброшенная информация может быть перехвачена и обработана соответствующим образом с помощью блока catch. Любой код, который должен быть обязательно выполнен при выходе из блока try, помещается в блок finally. Рассмотрим пример, демонстрирующий, как отследить и перехватить исключение.

static void Main()
{
int  x = int.Parse(Console.ReadLine());
int y =1 / x; 
Console.WriteLine(y);
}

Перечислим, какие исключительные ситуации могут возникнуть:

  • пользователь может ввести нечисловое значение
  • если ввести значение 0, то произойдет деление на 0.
  • Создайте указанные исключительные ситуации и посмотрите, как отреагирует на них система.

    Задание. Переменные x и y объявлены целочисленными. Объясните, что будет выведено на экран, если замените их тип на double и ввести с клавиатуры значение х равное 0, и почему.

    Теперь попробуем обработать эти ситуации. Для этого изменим код следующим образом.

    static void Main()
    { 
      try
      {
        int x = int.Parse(Console.ReadLine());    
        int y =1 / x;
    Console.WriteLine("y={0}", y);
        Console.WriteLine("блок try выполнился успешно");
      }
      catch		// *
      {
        Console.WriteLine("возникла какая-то ошибка");
      }
      Console.WriteLine("конец программы");
    }

    Рассмотрим, как обрабатываются исключения в данном примере. Когда возникает исключение, выполнение программы останавливается и управление передается блоку catch. Этот блок никогда не возвращает управление в то место программы, где возникло исключение. Поэтому команды из блока try, расположенные ниже строки, в которой возникло исключение, никогда не будут выполнены. Блок catch обрабатывает исключение, и выполнение программы продолжается с оператора, следующего за этим блоком.

    В нашем случае при вводе нечислового значения или 0 будет выведено сообщение "возникла ошибка", а затем сообщение "конец программы".

    Обработчик исключений позволяет не только отловить ошибку, но и вывести полную информацию о ней. Для демонстрации сказанного заменим блок catch следующим фрагментом.

    catch (Exception error)
    {
      Console.WriteLine("Возникла ошибка {0}", error);
    }

    Теперь, если возникнет исключительная ситуация, "выброшенная" информация будет записана в идентификатор error. Данную информацию можно просмотреть с помощью метода WriteLine. Такое сообщение очень полное и будет полезно только разработчику на этапе отладки проекта.

    Для пользователя на этапе эксплуатации приложения достаточно более краткой информации о типе ошибке. С этой целью в С# выделены стандартные классы исключений, такие как DivideByZeroException, FormatException. Внесем изменения в программу.

    static void Main()
    { 
      try
      {
        int x = int.Parse(Console.ReadLine());    // 1 ситуация              
        int y =1 / x;   // 2 ситуация
        Console.WriteLine("y={0}", y);
        Console.WriteLine("блок try выполнился успешно");
      }
      catch(FormatException)  // обработка 1 ситуации
      {
        Console.WriteLine("Ошибка: введено нечисловое значение!");                
      }
      catch (DivideByZeroException) // обработка 2 ситуации
      {
        Console.WriteLine("Ошибка: деление на 0!");
      }
      Console.WriteLine("конец программы");
    }

    В данном примере обрабатывается каждая ситуация в отдельности, при этом пользователю сообщается лишь минимальная информация об ошибке. В следующей таблице содержится описание наиболее часто используемых обработчиков стандартных исключений.

    Имя Описание
    ArithmeticException Ошибка в арифметических операциях или преобразованиях
    ArrayTypeMismatchException Попытка сохранения в массиве элемента несовместимого типа
    DivideByZeroException Попытка деления на ноль
    FormatException Попытка передать в метод аргумент неверного формата
    IndexOutOfRangeException Индекс массива выходит за границу диапазона
    InvalidCastException Ошибка преобразования типа
    OutOfMemoryException Недостаточно памяти для нового объекта
    OverflowException Переполнение при выполнении арифметических операций
    StackOverflowException Переполнение стека

    Одно из основных достоинств обработки исключений состоит в том, что она позволяет программе отреагировать на ошибку и продолжить выполнение. Рассмотрим программу, которая строит таблицу значений для функции вида y(x)=100/(x2-1).

    static void Main()
    {
     Console.WriteLine("a=");
     int a = int.Parse( Console.ReadLine());
     Console.WriteLine("b=");
     int b = int.Parse(Console.ReadLine());
     for (int i = a; i <= b; ++i)
     {
      try
      {
       Console.WriteLine("y({0})={1}", i, 100 / (i * i - 1));
      }
      catch (DivideByZeroException)
      {
       Console.WriteLine("y({0})=Деление на 0", i);
      }
     }
    }

    Если встречается деление на нуль, генерируется исключение типа DivideByZeroException. В программе это исключение обрабатывается выдачей сообщения об ошибке, после чего выполнение программы продолжается. При этом попытка разделить на нуль не вызывает внезапную динамическую ошибку (т.к. блок обработки прерываний помещен внутрь цикла for ). Вместо этого исключение позволяет красиво выйти из ошибочной ситуации и продолжить выполнение программы.

    Операторы checked и unchecked

    В С# предусмотрено специальное средство, которое связано с генерированием исключений, вызванных переполнением результата в арифметических вычислениях. Например, когда значение арифметического выражения выходит за пределы диапазона, определенного для типа данных выражения. Рассмотрим небольшой фрагмент программы:

    static void Main()
    {
     byte x = 200; byte y = 200;
     byte result = (byte) (x + y);
     Console.WriteLine(result);
    }

    Здесь сумма значений а и b превышает диапазон представления значений типа byte. Следовательно, результат данного выражения не может быть записан в переменную result, тип которой byte.

    Для управления подобными исключениями в С# используются операторы checked и unchecked. Чтобы указать, что некоторое выражение должно быть проконтролировано на предмет переполнения, используйте ключевое слово checked. А чтобы проигнорировать переполнение, используйте ключевое слово unchecked. В последнем случае результат будет усечен так, чтобы его тип соответствовал типу-результату выражения.

    Замечание. По умолчанию проверка переполнения отключена (галочка не стоит). В результате код выполняется быстро, но тогда программист должен быть уверен, что переполнения не случится или предусмотреть его возникновение. Как мы уже упоминали, можно включить проверку переполнения для всего проекта, однако она не всегда нужна. С помощью использования операторов checked и unchecked в С# реализуется механизм гибкого управления проверкой

    Можно задать (или отключить) проверку переполнения сразу для всего проекта. Для этого необходимо выполнить следующие действия

  • Щелкнуть правой кнопкой мыши на имени проекта
  • В выпадающем меню выбрать Properties
  • В появившемся окне (см. рис.) выбрать слева страницу Build
  • Щелкнуть на кнопке Advanced
  • В появившемся окошке поставить или убрать галочку напротив Check for arithmetic overflow/underflow property.
  • Оператор checked имеет две формы:

  • проверяет конкретное выражение и называется операторной checked-формой
    checked ((тип-выражения) expr)

    где expr — выражение, значение которого необходимо контролировать. Если значение контролируемого выражения переполнилось, генерируется исключение типа OverflowException.

  • проверяет блок инструкций
    checked 
    {
     // Инструкции, подлежащие проверке.
    }
  • Оператор unchecked также имеет две формы:

  • операторная форма, которая позволяет игнорировать переполнение для заданного выражения
    unchecked ((тип-выражения) expr)

    где ехрr — выражение, которое не проверяется на предмет переполнения. В случае переполнения это выражение усекается.

  • игнорирует переполнение, которое возможно в блоке инструкций
    unchecked 
    {
     // Инструкции, для которых переполнение игнорируется.
    }
  • Рассмотрим пример программы, которая демонстрирует использование checked и unchecked.

    static void Main()
    {
     byte x = 200; byte y = 200;
     try
     {
      byte result = unchecked((byte)(x + y)); 
      Console.WriteLine("1: {0}", result);
      result = checked((byte)(x + y));
      Console.WriteLine("2: ", result);
     }
     catch (OverflowException)
     {
      Console.WriteLine("возникло переполнение");
     }
    }

    Результат выполнения программы:

    1: 144
    возникло переполнение
    Задание. Уберите блок uncheсked. Посмотрите, что изменится в выполнении программы и дайте этому объяснение

    В данном примере мы посмотрели, как использовать checked и uncheсked для проверки выражения. А теперь посмотрим, как использовать их для контроля за блоком инструкций.

    static void Main()
    {
     byte n = 1; byte i;
     try
     {
      unchecked	//блок без проверки
      {
       for (i = 1; i < 10; i++) n *= i;
       Console.WriteLine("1: {0}", n);
      }
      checked 	//блок с проверкой
      {
       n=1;
       for (i = 1; i < 10; i++) n *= i;
       Console.WriteLine("2: ", n);
      }
     }
     catch (OverflowException)
     {
      Console.WriteLine("возникло переполнение");
     }
    }

    Результат выполнения программы:

    1: 128
    возникло переполнение
    Задание. Поменяйте местами блоки unchecked и checked. Посмотрите, что изменится в выполнении программы и дайте этому объяснение

    Генерация собственных исключений

    До сих пор мы рассматривали исключения, которые генерирует среда, но сгенерировать исключение может и сам программист. Для этого необходимо воспользоваться оператором throw, указав параметры, определяющие вид исключения. Параметром должен быть объект, порожденный от стандартного класса System.Exception. Этот объект используется для передачи информации об исключении обработчику.

    static void Main()
    {
     try
     {
      int x = int.Parse(Console.ReadLine());
      if (x < 0) throw new Exception(); //1
      Console.WriteLine("ok");
     }
     catch 
     {
      Console.WriteLine("введено недопустимое значение");
     }
    }

    В строчке 1 c помощью команды new был создан объект исключения типа Exception. При необходимости можно генерировать исключение любого типа.

    При генерации исключения можно определить сообщение, которое будет "выбрасываться" обработчиком исключений. Например:

    static void Main()
    {
     try
     {
      int x = int.Parse(Console.ReadLine());
      if (x < 0) throw new Exception("введено недопустимое значение"); //1
      Console.WriteLine("ok");
     }
     catch (Exception error)
     {
      Console.WriteLine(error.Message);
     } 
    }

    Полезные совет

    Рассмотрим несколько полезных приемов использования обработчиков исключений.

    Пример 1. Один try -блок можно вложить в другой. Исключение, сгенерированное во внутреннем try -блоке и не перехваченное catch -инструкцией, которая связана с этим try -блоком, передается во внешний try -блок. Например, в следующей программе исключение типа ArithmeticException перехватывается не внутренним try -блоком, а внешним.

    static void Main()
    {
     Console.WriteLine("a=");
     byte a = byte.Parse(Console.ReadLine());
     Console.WriteLine("b=");
     byte b = byte.Parse(Console.ReadLine());
     int f=1;
     try //Внешний блок-try
     {
      for (byte i = a; i <= b; ++i)
      {
       try //Внутренний блок-try
       {
        f=checked((int)(f*i));
        Console.WriteLine("y({0})={1:f6}", i, 100 / (f - 1));
       }
       catch (DivideByZeroException)
       {
        Console.WriteLine("y({0})=Деление на 0", i);
       }
      }
     }
     catch (ArithmeticException)
     {
      Console.WriteLine("ERROR");
     }
    }

    Использование вложенных try -блоков обусловлено желанием обрабатывать различные категории ошибок различными способами. Одни типы ошибок носят катастрофический характер и не подлежат исправлению. Другие — неопасны для дальнейшего функционирования программы, и с ними можно справиться прямо на месте их возникновения. Поэтому внешний try -блок можно использовать для перехвата самых серьезных ошибок, позволяя внутренним try -блокам обрабатывать менее опасные.

    Пример 2. Исключение, перехваченное одной catch -инструкцией, можно сгенерировать повторно, чтобы обеспечить возможность его перехвата другой (внешней) catch -инструкцией. Это позволяет нескольким обработчикам получить доступ к исключению.

    static void genException ()
    {
     Console.WriteLine("a=");
     double a = double.Parse(Console.ReadLine());
     Console.WriteLine("b=");
     double b = double.Parse(Console.ReadLine());
     int f = 1;
     try //Внешний блок-try
     { 
      for (double i = a; i <= b; ++i)
      {
       try //Внутренний блок-try
       {
        f = checked((int)(f * i));
        Console.WriteLine("y({0})={1:f6}", i, 100 / (f - 1));
      }
      catch (DivideByZeroException)
      {
       Console.WriteLine("y({0})=Деление на 0", i);
       }
      }
     }
     catch (ArithmeticException)
     {
      Console.WriteLine("ERROR");
      throw ; //повторная генерация исключения
     }
    }
    
    static void Main()
    {
     try
     {
      genException();
     }
     catch
     {
      Console.WriteLine("НЕИСПРАВИМАЯ ОШИБКА!!!");
     }
    }

    Нужно помнить, что при повторном генерировании исключения оно не будет повторно перехватываться той же catch -инструкцией, а передается следующей (внешней) catch -инструкции.

    Задания.
  • Объясните почему не было сгенерировано исключение DivideByZeroException.
  • Сгенерируйте собственное исключение DivideByZeroException и его обработку для ситуации f-1<0.000001.
  • Пример 3.Как упоминалось выше, тип исключения должен совпадать с типом, заданным в catch -инструкции. В противном случае это исключение не будет перехвачено. Можно перехватывать все исключения, используя catch -инструкцию без параметров. Кроме того, с try -блоком можно связать не одну, а несколько catch -инструкций. В этом случае все catch-инструкции должны перехватывать исключения различного типа. Если вы все же не уверены, что предусмотрели все ситуации, то последней можно добавить catch -инструкцию без параметров.

    Замечание. Иногда возникает потребность в обязательном выполнении каких-то действий, которые должны выполниться по выходу из try/catch -блока. Например, генерируется исключение и происходит преждевременное завершение выполнения программного фрагмента, но при этом остается открытым файл. Для выхода из такой ситуации С# предоставляет блок finally, который добавляется после всех блоков catch.
    static void Main()
    {
     for (int i = 0; i < 5; i++)
     {
      try
      {
       Console.WriteLine("Введите два числа");
       int a = int.Parse(Console.ReadLine());
       int b = int.Parse(Console.ReadLine());
       Console.WriteLine(a+"/"+b+"="+a/b);
      }
      catch (FormatException)
      {
       Console.WriteLine("Нужно ввести число!");
      }
      catch (DivideByZeroException)
      {
       Console.WriteLine("Делить на нуль нельзя!");
      }
      catch 
      {
       Console.WriteLine("Какая-то ошибка");
      }
      finally
      {
       Console.WriteLine("после try-блока");
      }
     }
    }
    Задание. Протестируйте данную программу, вводя поочередно следующие значения:
    a=4, b=2
    a=3, b=g
    a=d, b=1
    a=2, b=0,
    a=123456789987654321, b=1

    Практикум

    Постройте таблицу значений функции $$y=f(x)$$ для $$х \in [a, b]$$ с шагом $$h$$. Если в некоторой точке $$x$$ функция не определена, то выведите на экран сообщение об этом.

    Замечание. При решении данной задачи использовать вспомогательный метод $$f(x)$$, реализующий заданную функцию, а также проводить обработку возможных исключений.

  • $$y=\cfrac{1}{(1+x)^2}$$

    Пример:

    using System;
    namespace Hello
    {
     class Program
     {
      static double f(double x)
      {
       try
       {
        //если х не попадает в область определения, то генерируется исключение 
        if (x == -1) throw new Exception();  
        else return 1 / Math.Pow(1 + x, 2);
       }
       catch
       {
        throw;
       }
      }
      
      static void Main(string[] args)
      {
       try
       {
        Console.Write("a=");
        double a = double.Parse(Console.ReadLine());
        Console.Write("b=");
        double b = double.Parse(Console.ReadLine());
        Console.Write("h=");
        double h = double.Parse(Console.ReadLine());
        for (double i = a; i <= b; i += h)
        try
        {
         Console.WriteLine("y({0})={1:f4}", i, f(i));
        }
        catch
        {
         Console.WriteLine("y({0})=error", i);
        }    
       }
       catch (FormatException)
       {
        Console.WriteLine("Неверный формат ввода данных");
       }
       catch
       {
        Console.WriteLine("Неизвестная ошибка");
       }
      }
     }
    }
  • 2. $$y=\cfrac{1}{x^2-1}$$ ; 3. $$y=\sqrt{x^2-1}$$ ;
    4. $$y=\sqrt{5-x^3}$$ ; 5. $$y=\ln{(x-1)}$$ ; 6. $$y=\ln{(4-x^2)} $$ ;
    7. $$y=\cfrac{x}{\sqrt{2x-1}}$$ ; 8. $$y=\cfrac{3x+4}{\sqrt{x^2+2x+1}}$$ ; 9. $$y=\cfrac{1}{x-1}+\cfrac{2}{1-4x}$$ ;
    10. $$y=\ln{|x-2|}$$ ; 11. $$y=\ln{\cfrac{x}{x-2}}$$ ; 12. $$y=\ln{(x^4-1)}\ln{(1+x)}$$ ;
    13. $$y=\cfrac{\ln{(x-2)}}{\sqrt{5x+1}}$$ ; 14. $$y=\cfrac{\sqrt{x^2-2x+1}}{\ln{(4-2x)}}$$ ; 15. $$y=\ln{|3x|}\sqrt{2x^5-1}$$ ;
    16. $$y=\cfrac{3}{|x^3+8|}$$ ; 17. $$y=\cfrac{x+4}{x^2-2}+\sqrt{x^3-1}$$ ; 18. $$y=\sqrt{x^2+1}+\sqrt{x^2+5}$$ ;
    19. $$y=\cfrac{\sqrt{x^3-1}}{\sqrt{x^2-1}}$$ ; 20. $$y=\cfrac{1}{x+7}+\ln{(1-|x|)}$$ ;

    Самостоятельная работа

    Теоретический материал

    Вычисление конечных сумм и произведений

    Решение многих задач связано с нахождением суммы или произведения элементов заданной последовательности. В данном разделе мы рассмотрим основные приемы вычисления конечных сумм и произведений.

    Пусть $$u_{1}(x), u_{2}(x), \dots, u_{n}(x)$$ - произвольная последовательность n функций. Будем рассматривать конечную сумму вида $$u_{1}(x) + u_{2}(x) + \dots + u_{n}(x)$$. Такую сумму можно записать более компактно, используя следующее обозначение: $$u_1(x) + u_2(x) + … + u_n(x) = \sum_{i=1}^n{u_i(x)}$$. При $$n \le 0$$ значение суммы равно 0.

    В дальнейшем будем также использовать сокращенную запись для конечного произведения данной последовательности, которая выглядит следующим образом: $$u_1(x) \cdot u_2(x) \cdot … \cdot u_n(x) = \prod_{i=1}^n{u_i(x)}$$.

  • Написать программу, которая подсчитывает сумму натуральных чисел от $$1$$ до $$n$$ ( $$n \ge 1$$ ).Указания по решению задачи. Пусть $$s_{n}$$ - сумма натуральных чисел от $$1$$ до $$n$$. Тогда $$s_{n}=1+2+ \dots +(n-1)+n=(1+2+ \dots +(n-1))+n=s_{n-1}+n, s_{0}=0$$. Мы пришли к рекуррентному соотношению $$s_{0}=0, s_{n}=s_{n-1}+n$$, которым мы можем воспользоваться для подсчета суммы. Соотношение $$s_{n}=s_{n-1}+n$$ говорит о том, что сумма на $$n$$ -ном шаге равна сумме, полученной на предыдущем шаге, плюс очередное слагаемое.
    static void Main()
    {
     Console.Write("Ввведите значение n: ");
     int n=int.Parse(Console.ReadLine());
     int s=0;
     for (int i=1; i<=n; ++i)
      s+=i;
     Console.WriteLine("s="+s);
    }
  • Написать программу, которая подсчитывает $$n$$! для вещественного $$x$$ и натурального $$n$$.Указание по решению задачи. Из свойства факториала $$0!=1!=1$$, $$n!=1*2*3*…*n$$, $$n!=(n-1)!n$$. Следовательно, факториал можно вычислять, используя рекуррентное соотношение $$b_{0}=1, b_{n}=b_{n-1}*n$$.
    static void Main()
    {
     Console.Write("Ввведите значение n: ");
     int n=int.Parse(Console.ReadLine());
     int f=1;
     for (int i=1; i<=n; ++i)
     f*=i;
     Console.WriteLine("{0}!={1}", n, f);
    }
  • Написать программу для подсчета суммы $$S_n=\frac{\cos{x}}{1}+\frac{\cos{x}+\cos{2x}}{2}+\frac{\cos{x}+\cos{2x}+\cos{3x}}{3}+…+\frac{\cos{x}+…+\cos{nx}}{n}$$, где $$х $$ - вещественное число, $$n $$ - натуральное число.Указания по решению задачи. Если пронумеровать слагаемые, начиная с $$1$$, то мы увидим, что номер слагаемого совпадает со значением знаменателя. Рассмотрим каждый числитель отдельно: $$b_{1}=\cos{(x)}, b_{2}=\cos{(x)}+ \cos{(2x)}, b_{3}=\cos{(x)}+ \cos{(2x)}+ \cos{(3x)}, \dots $$ Эту последовательность можно представить рекуррентным соотношением $$b_{0}=0, b_{n}=b_{n-1}+cosnx$$ (1). Теперь сумму можно представить следующим образом, $$S_n=\frac{b_1}{1}+\frac{b_2}{2}+\frac{b_3}{3}+…+\frac{b_n}{n}$$, а для нее справедливо рекуррентное соотношение $$S_{0}=0$$, $$S_{n}= S_{n-1}+b_{n}/n$$ (2). При составлении программы будем использовать формулы (1-2).
    static void Main()
    {
     Console.Write("Ввведите значение n: ");
     int n=int.Parse(Console.ReadLine());
     Console.Write("Ввведите значение x: ");
     double x=double.Parse(Console.ReadLine());
     double b=0, s=0;
     for (int i=1; i<=n; ++i)
     {
      b+=Math.Cos(i*x);
      s+=b/i;
     }
     Console.WriteLine("s={0:f2}",s);
    }
  • Написать программу для подсчета суммы $$S_n=\sum_{i=1}^{n}{\frac{(-1)^{i+1}x^i}{i!}}$$, где $$х $$ - вещественное число, $$n $$ - натуральное число.Указания по решению задачи.Перейдем от сокращенной формы записи к развернутой, получим$$S_n=\frac{x}{1!}-\frac{x^2}{2!}+ \frac{x^3}{3!}-…+ \frac{(-1)^{n+1}x^n}{n!}.$$ Каждое слагаемое формируется по формуле $$a_n=\frac{(-1)^{n+1}x^n}{n!}$$. Если в эту формулу подставить $$n=0$$, то получим $$a_0=\frac{(-1)^{1}x^0}{0!}=-1$$.

    Чтобы не вводить несколько рекуррентных соотношений (отдельно для числителя, отдельно для знаменателя), представим общий член последовательности слагаемых с помощью рекуррентного соотношением вида $$a_{n} =a_{n-1}q$$, где $$q$$ для нас пока не известно. Найти его можно из выражения $$q=\frac{a_n}{a_{n-1}}$$. Произведя необходимые расчеты, получим, что $$q=-\frac{x}{i}}$$. Следовательно, для последовательности слагаемых мы получили рекуррентное соотношение $$a_{0}=1$$, $$a_i=-a_{i-1}\cdot \frac{x}{i}}$$ (3). А всю сумму, по аналогии с предыдущими примерами, можно представить рекуррентным соотношением: $$S_{0}=0$$, $$S_{n}= S_{n-1}+a_{n}$$ (4). Таким образом, при составлении программы будем пользоваться формулами (3-4).

    using System;
    
    namespace Hello
    {
     class Program
     {
      static void Main()
      {
       Console.Write("Ввведите значение n: ");
       int n=int.Parse(Console.ReadLine());
       Console.Write("Ввведите значение x: ");
       double x=double.Parse(Console.ReadLine());
       double a=-1, s=0;
       for (int i=1; i<=n; ++i)
       {
        a*=-x/i;  s+=a;
       }
       Console.WriteLine("s={0:f2}",s);
      }
     }
    }
  • Вычисление бесконечных сумм

    Будем теперь рассматривать бесконечную сумму вида $$u_1(x) + u_2(x) + … + u_n(x) +…= \sum_{i=1}^{\infty}{u_i(x)}$$. Это выражение называется функциональным рядом. При различных значениях $$x $$ из функционального ряда получаются различные числовые ряды $$a_1 + a_2 + … + a_n +…= \sum_{i=1}^{\infty}{a_i}$$. Числовой ряд может быть сходящимся или расходящимся. Совокупность значений $$x$$, при которой функциональный ряд сходится, называется его областью сходимости.

    Числовой ряд называется сходящимся, если сумма n первых его членов $$S_{n}=a_{1}+ a_{2}+ \dots + a_{n} + \dots $$ при $$n\to\infty$$ имеет предел, в противном случае, ряд называется расходящимся. Ряд может сходиться лишь при условии, что общий член ряда $$a_{n}$$ при неограниченном увеличении его номера стремится к нулю: $$\lim_{n\to\infty}{a_n}=0$$. Это необходимый признак сходимости для всякого ряда.

    В случае бесконечной суммы будем вычислять ее с заданной точностью $$e$$. Cчитается, что требуемая точность достигается, если вычислена сумма нескольких первых слагаемых и очередное слагаемое оказалось по модулю меньше чем е, то есть это слагаемое на результат практически не влияет. Тогда его и все последующие слагаемые можно не учитывать.

    Пример. Написать программу для подсчета суммы $$\sum_{i=1}^{\infty}{\frac{(-1)^i}{i!}}$$ с заданной точностью $$е$$ ( $$е > 0$$ ).

    Указание по решению задачи. Рассмотрим, что представляет из себя заданный ряд: $$\sum_{i=1}^{\infty}{\frac{(-1)^i}{i!}}=-\frac{1}{1}+\frac{1}{2}-\frac{1}{6}+\frac{1}{24}-\frac{1}{120}+…+\frac{1}{\infty}$$. Как видим, общий член ряда с увеличением значения i стремится к нулю. Поэтому данную сумму можно вычислить, но только с определенной точностью $$e$$. Заметим также, что последовательность слагаемых можно выразить с помощью рекуррентного соотношения $$a_{1}=-1$$, $$a_i=-\frac{ a_{i-1} }{i}$$, а всю сумму - с помощью рекуррентного соотношения $$S_{0}=0, S_{n}=S_{n-1}+a_{n}$$. (Данные рекуррентные соотношения выведите самостоятельно.)
    using System;
    namespace Hello
    {
     class Program
     {
      static void Main()
      {
       Console.Write("Задайте точность вычислений е: ");
       double e=double.Parse(Console.ReadLine());
       double a=-1, s=0;
       for (int i=2; Math.Abs(a)>=e; ++i)
       {
        s+=a;
        a/=-i;
       }
       Console.WriteLine("s={0:f2}",s);
      }
     }
    }

    Практическое задание

    Замечание. При решении задач производить обработку следующих исключительных ситуаций: ввода пользователем недопустимых значений и переполнения при вычислении математических выражений.
  • Для заданного натурального $$n $$ и действительного $$х$$ подсчитать следующие суммы:
  • $$S=1+\frac{1}{\sqrt{2}}+\frac{1}{\sqrt{3}}+…++\frac{1}{\sqrt{n}}$$ ;
  • $$S=\sin{x}+\sin{\sin{x}}+\sin{\sin{\sin{x}}}+…+\underbrace{\sin{\sin{\sin{\sin{…\sin{x}}}}}}_{n \text{ раз}} $$ ;
  • $$S = 1!+2!+3!+ \dots +n$$!;
  • $$S=-\frac{1}{2}+\frac{1}{2^2}-\frac{1}{2^3}+…+\frac{(-1)^n}{2^n}$$
  • Для заданного натурального $$k$$ и действительного $$x $$ подсчитать следующие выражения:
  • $$S=\sum_{n=1}^{k}{\frac{x^n}{n}}$$
  • $$S=\sum_{n=1}^{k}{\frac{(-1)^{n+1}x^{2n-1}}{(2n-1)!}}$$
  • $$P=\prod_{n=1}^{k}{\left( 1+\frac{x^{2n+1}}{n(n+1)}\right)}$$
  • $$P=\prod_{n=2}^{k}{\left( 1+\frac{(-1)^{n}x^{2n-1}}{n^3-1}\right)}$$
  • Вычислить бесконечную сумму ряда с заданной точностью е (e>0).
  • $$\sum_{i=1}^{\infty}{\frac{1}{i^2}}$$
  • $$\sum_{i=1}^{\infty}{\frac{1}{3^i+4^i}}$$
  • $$\sum_{i=1}^{\infty}{\frac{(-1)^1}{(2i-1)!}}$$
  • $$\sum_{i=1}^{\infty}{\frac{(-1)^{i+1}}{3^{2i-1}}}$$
  • Вычислить и вывести на экран значение функции $$F(x) $$ на отрезке $$[a,b]$$ c шагом $$h=0.1$$ с точностью $$e$$.Результат работы программы представить в виде следующей таблицы:
  • Значение x Значение функции F(x) Количество просуммированных слагаемых n
    1
    2
    Замечание. При решении задачи использовать вспомогательную функцию.
  • $$F(x) = 1+\frac{x^2}{4}+\frac{x^3}{4^2}+\frac{x^4}{4^3}+\frac{x^5}{4^4}+…, x\in [0,1;0,9]$$
  • $$F(x) = 1+\frac{x^3}{3\cdot 2}+\frac{x^5}{5\cdot 2^2}+\frac{x^7}{7\cdot 2^3}…, x\in [0,1;0,99]$$
  • $$F(x) = 1-\frac{x^2}{3!}+\frac{x^4}{5!}-\frac{x^6}{7!}+ \frac{x^8}{9!}…, x\in [0;1]$$
  • $$F(x) = \frac{x-1}{x}+\frac{(x-1)^2}{2x^2}+\frac{(x-1)^3}{3x^3}+…, x\in [1;2]$$
  • Страницы:

    Язык С#, как и многие другие объектно-ориентированные языки, реагирует на ошибки и ненормальные ситуации с помощью механизма обработки исключений.Исключение - это объект, генерирующий информацию о "необычном программном происшествии". При этом важно проводить различие между ошибкой в программе, ошибочной ситуацией и исключительной ситуацией.

    Ошибка в программе допускается программистом при ее разработке. Например, вместо операции сравнения ( == ) используется операция присваивания ( = ). Программист должен исправить подобные ошибки до передачи кода программы заказчику. Использование механизма обработки исключений не является защитой от ошибок в программе.

    Ошибочная ситуация вызвана действиями пользователя. Например, пользователь вместо числа ввел строку. Такая ошибка способна вызывать исключение. Программист должен предвидеть ошибочные ситуации и предотвращать их с помощью операторов, проверяющих допустимость поступающих данных.

    Даже если программист исправил все свои ошибки в программе, предвидел все ошибочные ситуации, он все равно может столкнуться с непредсказуемыми и неотвратимыми проблемами - исключительными ситуациями. Например, нехваткой доступной памяти или попыткой открыть несуществующий файл. Исключительные ситуации программист предвидеть не может, но он может отреагировать на них так, что они не приведут к краху программы.

    Для обработки ошибочных и исключительных ситуаций в С# используется специальная подсистема обработки исключений. Преимущество данной подсистемы состоит в автоматизации создания большей части кода по обработке исключений. Раньше этот код приходилось вводить в программу "вручную". Кроме этого обработчик исключений способен распознавать и выдавать информацию о таких стандартных исключениях, как деление на нуль или попадание вне диапазона определения индекса.

    Оператор try

    В С# исключения представляются классами. Все классы исключений порождены от встроенного класса исключений Exception, который определен в пространстве имен System.

    Управление обработкой исключений основывается на использовании оператора try. Синтаксис оператора:

    try	// контролируемый блок
    { 
    …
    }
    catch //один или несколько блоков обработки исключений
    { 
    …
    }
    finally	//блок завершения
    {
    …
    }

    Программные инструкции, которые нужно проконтролировать на предмет исключений, помещаются в блок try. Если исключение возникает в этом блоке, оно дает знать о себе выбросом определенного рода информации. Выброшенная информация может быть перехвачена и обработана соответствующим образом с помощью блока catch. Любой код, который должен быть обязательно выполнен при выходе из блока try, помещается в блок finally. Рассмотрим пример, демонстрирующий, как отследить и перехватить исключение.

    static void Main()
    {
    int  x = int.Parse(Console.ReadLine());
    int y =1 / x; 
    Console.WriteLine(y);
    }

    Перечислим, какие исключительные ситуации могут возникнуть:

  • пользователь может ввести нечисловое значение
  • если ввести значение 0, то произойдет деление на 0.
  • Создайте указанные исключительные ситуации и посмотрите, как отреагирует на них система.

    Задание. Переменные x и y объявлены целочисленными. Объясните, что будет выведено на экран, если замените их тип на double и ввести с клавиатуры значение х равное 0, и почему.

    Теперь попробуем обработать эти ситуации. Для этого изменим код следующим образом.

    static void Main()
    { 
      try
      {
        int x = int.Parse(Console.ReadLine());    
        int y =1 / x;
    Console.WriteLine("y={0}", y);
        Console.WriteLine("блок try выполнился успешно");
      }
      catch		// *
      {
        Console.WriteLine("возникла какая-то ошибка");
      }
      Console.WriteLine("конец программы");
    }

    Рассмотрим, как обрабатываются исключения в данном примере. Когда возникает исключение, выполнение программы останавливается и управление передается блоку catch. Этот блок никогда не возвращает управление в то место программы, где возникло исключение. Поэтому команды из блока try, расположенные ниже строки, в которой возникло исключение, никогда не будут выполнены. Блок catch обрабатывает исключение, и выполнение программы продолжается с оператора, следующего за этим блоком.

    В нашем случае при вводе нечислового значения или 0 будет выведено сообщение "возникла ошибка", а затем сообщение "конец программы".

    Обработчик исключений позволяет не только отловить ошибку, но и вывести полную информацию о ней. Для демонстрации сказанного заменим блок catch следующим фрагментом.

    catch (Exception error)
    {
      Console.WriteLine("Возникла ошибка {0}", error);
    }

    Теперь, если возникнет исключительная ситуация, "выброшенная" информация будет записана в идентификатор error. Данную информацию можно просмотреть с помощью метода WriteLine. Такое сообщение очень полное и будет полезно только разработчику на этапе отладки проекта.

    Для пользователя на этапе эксплуатации приложения достаточно более краткой информации о типе ошибке. С этой целью в С# выделены стандартные классы исключений, такие как DivideByZeroException, FormatException. Внесем изменения в программу.

    static void Main()
    { 
      try
      {
        int x = int.Parse(Console.ReadLine());    // 1 ситуация              
        int y =1 / x;   // 2 ситуация
        Console.WriteLine("y={0}", y);
        Console.WriteLine("блок try выполнился успешно");
      }
      catch(FormatException)  // обработка 1 ситуации
      {
        Console.WriteLine("Ошибка: введено нечисловое значение!");                
      }
      catch (DivideByZeroException) // обработка 2 ситуации
      {
        Console.WriteLine("Ошибка: деление на 0!");
      }
      Console.WriteLine("конец программы");
    }

    В данном примере обрабатывается каждая ситуация в отдельности, при этом пользователю сообщается лишь минимальная информация об ошибке. В следующей таблице содержится описание наиболее часто используемых обработчиков стандартных исключений.

    Имя Описание
    ArithmeticException Ошибка в арифметических операциях или преобразованиях
    ArrayTypeMismatchException Попытка сохранения в массиве элемента несовместимого типа
    DivideByZeroException Попытка деления на ноль
    FormatException Попытка передать в метод аргумент неверного формата
    IndexOutOfRangeException Индекс массива выходит за границу диапазона
    InvalidCastException Ошибка преобразования типа
    OutOfMemoryException Недостаточно памяти для нового объекта
    OverflowException Переполнение при выполнении арифметических операций
    StackOverflowException Переполнение стека

    Одно из основных достоинств обработки исключений состоит в том, что она позволяет программе отреагировать на ошибку и продолжить выполнение. Рассмотрим программу, которая строит таблицу значений для функции вида y(x)=100/(x2-1).

    static void Main()
    {
     Console.WriteLine("a=");
     int a = int.Parse( Console.ReadLine());
     Console.WriteLine("b=");
     int b = int.Parse(Console.ReadLine());
     for (int i = a; i <= b; ++i)
     {
      try
      {
       Console.WriteLine("y({0})={1}", i, 100 / (i * i - 1));
      }
      catch (DivideByZeroException)
      {
       Console.WriteLine("y({0})=Деление на 0", i);
      }
     }
    }

    Если встречается деление на нуль, генерируется исключение типа DivideByZeroException. В программе это исключение обрабатывается выдачей сообщения об ошибке, после чего выполнение программы продолжается. При этом попытка разделить на нуль не вызывает внезапную динамическую ошибку (т.к. блок обработки прерываний помещен внутрь цикла for ). Вместо этого исключение позволяет красиво выйти из ошибочной ситуации и продолжить выполнение программы.

    Операторы checked и unchecked

    В С# предусмотрено специальное средство, которое связано с генерированием исключений, вызванных переполнением результата в арифметических вычислениях. Например, когда значение арифметического выражения выходит за пределы диапазона, определенного для типа данных выражения. Рассмотрим небольшой фрагмент программы:

    static void Main()
    {
     byte x = 200; byte y = 200;
     byte result = (byte) (x + y);
     Console.WriteLine(result);
    }

    Здесь сумма значений а и b превышает диапазон представления значений типа byte. Следовательно, результат данного выражения не может быть записан в переменную result, тип которой byte.

    Для управления подобными исключениями в С# используются операторы checked и unchecked. Чтобы указать, что некоторое выражение должно быть проконтролировано на предмет переполнения, используйте ключевое слово checked. А чтобы проигнорировать переполнение, используйте ключевое слово unchecked. В последнем случае результат будет усечен так, чтобы его тип соответствовал типу-результату выражения.

    Замечание. По умолчанию проверка переполнения отключена (галочка не стоит). В результате код выполняется быстро, но тогда программист должен быть уверен, что переполнения не случится или предусмотреть его возникновение. Как мы уже упоминали, можно включить проверку переполнения для всего проекта, однако она не всегда нужна. С помощью использования операторов checked и unchecked в С# реализуется механизм гибкого управления проверкой

    Можно задать (или отключить) проверку переполнения сразу для всего проекта. Для этого необходимо выполнить следующие действия

  • Щелкнуть правой кнопкой мыши на имени проекта
  • В выпадающем меню выбрать Properties
  • В появившемся окне (см. рис.) выбрать слева страницу Build
  • Щелкнуть на кнопке Advanced
  • В появившемся окошке поставить или убрать галочку напротив Check for arithmetic overflow/underflow property.
  • Оператор checked имеет две формы:

  • проверяет конкретное выражение и называется операторной checked-формой
    checked ((тип-выражения) expr)

    где expr — выражение, значение которого необходимо контролировать. Если значение контролируемого выражения переполнилось, генерируется исключение типа OverflowException.

  • проверяет блок инструкций
    checked 
    {
     // Инструкции, подлежащие проверке.
    }
  • Оператор unchecked также имеет две формы:

  • операторная форма, которая позволяет игнорировать переполнение для заданного выражения
    unchecked ((тип-выражения) expr)

    где ехрr — выражение, которое не проверяется на предмет переполнения. В случае переполнения это выражение усекается.

  • игнорирует переполнение, которое возможно в блоке инструкций
    unchecked 
    {
     // Инструкции, для которых переполнение игнорируется.
    }
  • Рассмотрим пример программы, которая демонстрирует использование checked и unchecked.

    static void Main()
    {
     byte x = 200; byte y = 200;
     try
     {
      byte result = unchecked((byte)(x + y)); 
      Console.WriteLine("1: {0}", result);
      result = checked((byte)(x + y));
      Console.WriteLine("2: ", result);
     }
     catch (OverflowException)
     {
      Console.WriteLine("возникло переполнение");
     }
    }

    Результат выполнения программы:

    1: 144
    возникло переполнение
    Задание. Уберите блок uncheсked. Посмотрите, что изменится в выполнении программы и дайте этому объяснение

    В данном примере мы посмотрели, как использовать checked и uncheсked для проверки выражения. А теперь посмотрим, как использовать их для контроля за блоком инструкций.

    static void Main()
    {
     byte n = 1; byte i;
     try
     {
      unchecked	//блок без проверки
      {
       for (i = 1; i < 10; i++) n *= i;
       Console.WriteLine("1: {0}", n);
      }
      checked 	//блок с проверкой
      {
       n=1;
       for (i = 1; i < 10; i++) n *= i;
       Console.WriteLine("2: ", n);
      }
     }
     catch (OverflowException)
     {
      Console.WriteLine("возникло переполнение");
     }
    }

    Результат выполнения программы:

    1: 128
    возникло переполнение
    Задание. Поменяйте местами блоки unchecked и checked. Посмотрите, что изменится в выполнении программы и дайте этому объяснение

    Генерация собственных исключений

    До сих пор мы рассматривали исключения, которые генерирует среда, но сгенерировать исключение может и сам программист. Для этого необходимо воспользоваться оператором throw, указав параметры, определяющие вид исключения. Параметром должен быть объект, порожденный от стандартного класса System.Exception. Этот объект используется для передачи информации об исключении обработчику.

    static void Main()
    {
     try
     {
      int x = int.Parse(Console.ReadLine());
      if (x < 0) throw new Exception(); //1
      Console.WriteLine("ok");
     }
     catch 
     {
      Console.WriteLine("введено недопустимое значение");
     }
    }

    В строчке 1 c помощью команды new был создан объект исключения типа Exception. При необходимости можно генерировать исключение любого типа.

    При генерации исключения можно определить сообщение, которое будет "выбрасываться" обработчиком исключений. Например:

    static void Main()
    {
     try
     {
      int x = int.Parse(Console.ReadLine());
      if (x < 0) throw new Exception("введено недопустимое значение"); //1
      Console.WriteLine("ok");
     }
     catch (Exception error)
     {
      Console.WriteLine(error.Message);
     } 
    }

    Полезные совет

    Рассмотрим несколько полезных приемов использования обработчиков исключений.

    Пример 1. Один try -блок можно вложить в другой. Исключение, сгенерированное во внутреннем try -блоке и не перехваченное catch -инструкцией, которая связана с этим try -блоком, передается во внешний try -блок. Например, в следующей программе исключение типа ArithmeticException перехватывается не внутренним try -блоком, а внешним.

    static void Main()
    {
     Console.WriteLine("a=");
     byte a = byte.Parse(Console.ReadLine());
     Console.WriteLine("b=");
     byte b = byte.Parse(Console.ReadLine());
     int f=1;
     try //Внешний блок-try
     {
      for (byte i = a; i <= b; ++i)
      {
       try //Внутренний блок-try
       {
        f=checked((int)(f*i));
        Console.WriteLine("y({0})={1:f6}", i, 100 / (f - 1));
       }
       catch (DivideByZeroException)
       {
        Console.WriteLine("y({0})=Деление на 0", i);
       }
      }
     }
     catch (ArithmeticException)
     {
      Console.WriteLine("ERROR");
     }
    }

    Использование вложенных try -блоков обусловлено желанием обрабатывать различные категории ошибок различными способами. Одни типы ошибок носят катастрофический характер и не подлежат исправлению. Другие — неопасны для дальнейшего функционирования программы, и с ними можно справиться прямо на месте их возникновения. Поэтому внешний try -блок можно использовать для перехвата самых серьезных ошибок, позволяя внутренним try -блокам обрабатывать менее опасные.

    Пример 2. Исключение, перехваченное одной catch -инструкцией, можно сгенерировать повторно, чтобы обеспечить возможность его перехвата другой (внешней) catch -инструкцией. Это позволяет нескольким обработчикам получить доступ к исключению.

    static void genException ()
    {
     Console.WriteLine("a=");
     double a = double.Parse(Console.ReadLine());
     Console.WriteLine("b=");
     double b = double.Parse(Console.ReadLine());
     int f = 1;
     try //Внешний блок-try
     { 
      for (double i = a; i <= b; ++i)
      {
       try //Внутренний блок-try
       {
        f = checked((int)(f * i));
        Console.WriteLine("y({0})={1:f6}", i, 100 / (f - 1));
      }
      catch (DivideByZeroException)
      {
       Console.WriteLine("y({0})=Деление на 0", i);
       }
      }
     }
     catch (ArithmeticException)
     {
      Console.WriteLine("ERROR");
      throw ; //повторная генерация исключения
     }
    }
    
    static void Main()
    {
     try
     {
      genException();
     }
     catch
     {
      Console.WriteLine("НЕИСПРАВИМАЯ ОШИБКА!!!");
     }
    }

    Нужно помнить, что при повторном генерировании исключения оно не будет повторно перехватываться той же catch -инструкцией, а передается следующей (внешней) catch -инструкции.

    Задания.
  • Объясните почему не было сгенерировано исключение DivideByZeroException.
  • Сгенерируйте собственное исключение DivideByZeroException и его обработку для ситуации f-1<0.000001.
  • Пример 3.Как упоминалось выше, тип исключения должен совпадать с типом, заданным в catch -инструкции. В противном случае это исключение не будет перехвачено. Можно перехватывать все исключения, используя catch -инструкцию без параметров. Кроме того, с try -блоком можно связать не одну, а несколько catch -инструкций. В этом случае все catch-инструкции должны перехватывать исключения различного типа. Если вы все же не уверены, что предусмотрели все ситуации, то последней можно добавить catch -инструкцию без параметров.

    Замечание. Иногда возникает потребность в обязательном выполнении каких-то действий, которые должны выполниться по выходу из try/catch -блока. Например, генерируется исключение и происходит преждевременное завершение выполнения программного фрагмента, но при этом остается открытым файл. Для выхода из такой ситуации С# предоставляет блок finally, который добавляется после всех блоков catch.
    static void Main()
    {
     for (int i = 0; i < 5; i++)
     {
      try
      {
       Console.WriteLine("Введите два числа");
       int a = int.Parse(Console.ReadLine());
       int b = int.Parse(Console.ReadLine());
       Console.WriteLine(a+"/"+b+"="+a/b);
      }
      catch (FormatException)
      {
       Console.WriteLine("Нужно ввести число!");
      }
      catch (DivideByZeroException)
      {
       Console.WriteLine("Делить на нуль нельзя!");
      }
      catch 
      {
       Console.WriteLine("Какая-то ошибка");
      }
      finally
      {
       Console.WriteLine("после try-блока");
      }
     }
    }
    Задание. Протестируйте данную программу, вводя поочередно следующие значения:
    a=4, b=2
    a=3, b=g
    a=d, b=1
    a=2, b=0,
    a=123456789987654321, b=1

    Практикум

    Постройте таблицу значений функции $$y=f(x)$$ для $$х \in [a, b]$$ с шагом $$h$$. Если в некоторой точке $$x$$ функция не определена, то выведите на экран сообщение об этом.

    Замечание. При решении данной задачи использовать вспомогательный метод $$f(x)$$, реализующий заданную функцию, а также проводить обработку возможных исключений.

  • $$y=\cfrac{1}{(1+x)^2}$$

    Пример:

    using System;
    namespace Hello
    {
     class Program
     {
      static double f(double x)
      {
       try
       {
        //если х не попадает в область определения, то генерируется исключение 
        if (x == -1) throw new Exception();  
        else return 1 / Math.Pow(1 + x, 2);
       }
       catch
       {
        throw;
       }
      }
      
      static void Main(string[] args)
      {
       try
       {
        Console.Write("a=");
        double a = double.Parse(Console.ReadLine());
        Console.Write("b=");
        double b = double.Parse(Console.ReadLine());
        Console.Write("h=");
        double h = double.Parse(Console.ReadLine());
        for (double i = a; i <= b; i += h)
        try
        {
         Console.WriteLine("y({0})={1:f4}", i, f(i));
        }
        catch
        {
         Console.WriteLine("y({0})=error", i);
        }    
       }
       catch (FormatException)
       {
        Console.WriteLine("Неверный формат ввода данных");
       }
       catch
       {
        Console.WriteLine("Неизвестная ошибка");
       }
      }
     }
    }
  • 2. $$y=\cfrac{1}{x^2-1}$$ ; 3. $$y=\sqrt{x^2-1}$$ ;
    4. $$y=\sqrt{5-x^3}$$ ; 5. $$y=\ln{(x-1)}$$ ; 6. $$y=\ln{(4-x^2)} $$ ;
    7. $$y=\cfrac{x}{\sqrt{2x-1}}$$ ; 8. $$y=\cfrac{3x+4}{\sqrt{x^2+2x+1}}$$ ; 9. $$y=\cfrac{1}{x-1}+\cfrac{2}{1-4x}$$ ;
    10. $$y=\ln{|x-2|}$$ ; 11. $$y=\ln{\cfrac{x}{x-2}}$$ ; 12. $$y=\ln{(x^4-1)}\ln{(1+x)}$$ ;
    13. $$y=\cfrac{\ln{(x-2)}}{\sqrt{5x+1}}$$ ; 14. $$y=\cfrac{\sqrt{x^2-2x+1}}{\ln{(4-2x)}}$$ ; 15. $$y=\ln{|3x|}\sqrt{2x^5-1}$$ ;
    16. $$y=\cfrac{3}{|x^3+8|}$$ ; 17. $$y=\cfrac{x+4}{x^2-2}+\sqrt{x^3-1}$$ ; 18. $$y=\sqrt{x^2+1}+\sqrt{x^2+5}$$ ;
    19. $$y=\cfrac{\sqrt{x^3-1}}{\sqrt{x^2-1}}$$ ; 20. $$y=\cfrac{1}{x+7}+\ln{(1-|x|)}$$ ;

    Самостоятельная работа

    Теоретический материал

    Вычисление конечных сумм и произведений

    Решение многих задач связано с нахождением суммы или произведения элементов заданной последовательности. В данном разделе мы рассмотрим основные приемы вычисления конечных сумм и произведений.

    Пусть $$u_{1}(x), u_{2}(x), \dots, u_{n}(x)$$ - произвольная последовательность n функций. Будем рассматривать конечную сумму вида $$u_{1}(x) + u_{2}(x) + \dots + u_{n}(x)$$. Такую сумму можно записать более компактно, используя следующее обозначение: $$u_1(x) + u_2(x) + … + u_n(x) = \sum_{i=1}^n{u_i(x)}$$. При $$n \le 0$$ значение суммы равно 0.

    В дальнейшем будем также использовать сокращенную запись для конечного произведения данной последовательности, которая выглядит следующим образом: $$u_1(x) \cdot u_2(x) \cdot … \cdot u_n(x) = \prod_{i=1}^n{u_i(x)}$$.

  • Написать программу, которая подсчитывает сумму натуральных чисел от $$1$$ до $$n$$ ( $$n \ge 1$$ ).Указания по решению задачи. Пусть $$s_{n}$$ - сумма натуральных чисел от $$1$$ до $$n$$. Тогда $$s_{n}=1+2+ \dots +(n-1)+n=(1+2+ \dots +(n-1))+n=s_{n-1}+n, s_{0}=0$$. Мы пришли к рекуррентному соотношению $$s_{0}=0, s_{n}=s_{n-1}+n$$, которым мы можем воспользоваться для подсчета суммы. Соотношение $$s_{n}=s_{n-1}+n$$ говорит о том, что сумма на $$n$$ -ном шаге равна сумме, полученной на предыдущем шаге, плюс очередное слагаемое.
    static void Main()
    {
     Console.Write("Ввведите значение n: ");
     int n=int.Parse(Console.ReadLine());
     int s=0;
     for (int i=1; i<=n; ++i)
      s+=i;
     Console.WriteLine("s="+s);
    }
  • Написать программу, которая подсчитывает $$n$$! для вещественного $$x$$ и натурального $$n$$.Указание по решению задачи. Из свойства факториала $$0!=1!=1$$, $$n!=1*2*3*…*n$$, $$n!=(n-1)!n$$. Следовательно, факториал можно вычислять, используя рекуррентное соотношение $$b_{0}=1, b_{n}=b_{n-1}*n$$.
    static void Main()
    {
     Console.Write("Ввведите значение n: ");
     int n=int.Parse(Console.ReadLine());
     int f=1;
     for (int i=1; i<=n; ++i)
     f*=i;
     Console.WriteLine("{0}!={1}", n, f);
    }
  • Написать программу для подсчета суммы $$S_n=\frac{\cos{x}}{1}+\frac{\cos{x}+\cos{2x}}{2}+\frac{\cos{x}+\cos{2x}+\cos{3x}}{3}+…+\frac{\cos{x}+…+\cos{nx}}{n}$$, где $$х $$ - вещественное число, $$n $$ - натуральное число.Указания по решению задачи. Если пронумеровать слагаемые, начиная с $$1$$, то мы увидим, что номер слагаемого совпадает со значением знаменателя. Рассмотрим каждый числитель отдельно: $$b_{1}=\cos{(x)}, b_{2}=\cos{(x)}+ \cos{(2x)}, b_{3}=\cos{(x)}+ \cos{(2x)}+ \cos{(3x)}, \dots $$ Эту последовательность можно представить рекуррентным соотношением $$b_{0}=0, b_{n}=b_{n-1}+cosnx$$ (1). Теперь сумму можно представить следующим образом, $$S_n=\frac{b_1}{1}+\frac{b_2}{2}+\frac{b_3}{3}+…+\frac{b_n}{n}$$, а для нее справедливо рекуррентное соотношение $$S_{0}=0$$, $$S_{n}= S_{n-1}+b_{n}/n$$ (2). При составлении программы будем использовать формулы (1-2).
    static void Main()
    {
     Console.Write("Ввведите значение n: ");
     int n=int.Parse(Console.ReadLine());
     Console.Write("Ввведите значение x: ");
     double x=double.Parse(Console.ReadLine());
     double b=0, s=0;
     for (int i=1; i<=n; ++i)
     {
      b+=Math.Cos(i*x);
      s+=b/i;
     }
     Console.WriteLine("s={0:f2}",s);
    }
  • Написать программу для подсчета суммы $$S_n=\sum_{i=1}^{n}{\frac{(-1)^{i+1}x^i}{i!}}$$, где $$х $$ - вещественное число, $$n $$ - натуральное число.Указания по решению задачи.Перейдем от сокращенной формы записи к развернутой, получим$$S_n=\frac{x}{1!}-\frac{x^2}{2!}+ \frac{x^3}{3!}-…+ \frac{(-1)^{n+1}x^n}{n!}.$$ Каждое слагаемое формируется по формуле $$a_n=\frac{(-1)^{n+1}x^n}{n!}$$. Если в эту формулу подставить $$n=0$$, то получим $$a_0=\frac{(-1)^{1}x^0}{0!}=-1$$.

    Чтобы не вводить несколько рекуррентных соотношений (отдельно для числителя, отдельно для знаменателя), представим общий член последовательности слагаемых с помощью рекуррентного соотношением вида $$a_{n} =a_{n-1}q$$, где $$q$$ для нас пока не известно. Найти его можно из выражения $$q=\frac{a_n}{a_{n-1}}$$. Произведя необходимые расчеты, получим, что $$q=-\frac{x}{i}}$$. Следовательно, для последовательности слагаемых мы получили рекуррентное соотношение $$a_{0}=1$$, $$a_i=-a_{i-1}\cdot \frac{x}{i}}$$ (3). А всю сумму, по аналогии с предыдущими примерами, можно представить рекуррентным соотношением: $$S_{0}=0$$, $$S_{n}= S_{n-1}+a_{n}$$ (4). Таким образом, при составлении программы будем пользоваться формулами (3-4).

    using System;
    
    namespace Hello
    {
     class Program
     {
      static void Main()
      {
       Console.Write("Ввведите значение n: ");
       int n=int.Parse(Console.ReadLine());
       Console.Write("Ввведите значение x: ");
       double x=double.Parse(Console.ReadLine());
       double a=-1, s=0;
       for (int i=1; i<=n; ++i)
       {
        a*=-x/i;  s+=a;
       }
       Console.WriteLine("s={0:f2}",s);
      }
     }
    }
  • Вычисление бесконечных сумм

    Будем теперь рассматривать бесконечную сумму вида $$u_1(x) + u_2(x) + … + u_n(x) +…= \sum_{i=1}^{\infty}{u_i(x)}$$. Это выражение называется функциональным рядом. При различных значениях $$x $$ из функционального ряда получаются различные числовые ряды $$a_1 + a_2 + … + a_n +…= \sum_{i=1}^{\infty}{a_i}$$. Числовой ряд может быть сходящимся или расходящимся. Совокупность значений $$x$$, при которой функциональный ряд сходится, называется его областью сходимости.

    Числовой ряд называется сходящимся, если сумма n первых его членов $$S_{n}=a_{1}+ a_{2}+ \dots + a_{n} + \dots $$ при $$n\to\infty$$ имеет предел, в противном случае, ряд называется расходящимся. Ряд может сходиться лишь при условии, что общий член ряда $$a_{n}$$ при неограниченном увеличении его номера стремится к нулю: $$\lim_{n\to\infty}{a_n}=0$$. Это необходимый признак сходимости для всякого ряда.

    В случае бесконечной суммы будем вычислять ее с заданной точностью $$e$$. Cчитается, что требуемая точность достигается, если вычислена сумма нескольких первых слагаемых и очередное слагаемое оказалось по модулю меньше чем е, то есть это слагаемое на результат практически не влияет. Тогда его и все последующие слагаемые можно не учитывать.

    Пример. Написать программу для подсчета суммы $$\sum_{i=1}^{\infty}{\frac{(-1)^i}{i!}}$$ с заданной точностью $$е$$ ( $$е > 0$$ ).

    Указание по решению задачи. Рассмотрим, что представляет из себя заданный ряд: $$\sum_{i=1}^{\infty}{\frac{(-1)^i}{i!}}=-\frac{1}{1}+\frac{1}{2}-\frac{1}{6}+\frac{1}{24}-\frac{1}{120}+…+\frac{1}{\infty}$$. Как видим, общий член ряда с увеличением значения i стремится к нулю. Поэтому данную сумму можно вычислить, но только с определенной точностью $$e$$. Заметим также, что последовательность слагаемых можно выразить с помощью рекуррентного соотношения $$a_{1}=-1$$, $$a_i=-\frac{ a_{i-1} }{i}$$, а всю сумму - с помощью рекуррентного соотношения $$S_{0}=0, S_{n}=S_{n-1}+a_{n}$$. (Данные рекуррентные соотношения выведите самостоятельно.)
    using System;
    namespace Hello
    {
     class Program
     {
      static void Main()
      {
       Console.Write("Задайте точность вычислений е: ");
       double e=double.Parse(Console.ReadLine());
       double a=-1, s=0;
       for (int i=2; Math.Abs(a)>=e; ++i)
       {
        s+=a;
        a/=-i;
       }
       Console.WriteLine("s={0:f2}",s);
      }
     }
    }

    Практическое задание

    Замечание. При решении задач производить обработку следующих исключительных ситуаций: ввода пользователем недопустимых значений и переполнения при вычислении математических выражений.
  • Для заданного натурального $$n $$ и действительного $$х$$ подсчитать следующие суммы:
  • $$S=1+\frac{1}{\sqrt{2}}+\frac{1}{\sqrt{3}}+…++\frac{1}{\sqrt{n}}$$ ;
  • $$S=\sin{x}+\sin{\sin{x}}+\sin{\sin{\sin{x}}}+…+\underbrace{\sin{\sin{\sin{\sin{…\sin{x}}}}}}_{n \text{ раз}} $$ ;
  • $$S = 1!+2!+3!+ \dots +n$$!;
  • $$S=-\frac{1}{2}+\frac{1}{2^2}-\frac{1}{2^3}+…+\frac{(-1)^n}{2^n}$$
  • Для заданного натурального $$k$$ и действительного $$x $$ подсчитать следующие выражения:
  • $$S=\sum_{n=1}^{k}{\frac{x^n}{n}}$$
  • $$S=\sum_{n=1}^{k}{\frac{(-1)^{n+1}x^{2n-1}}{(2n-1)!}}$$
  • $$P=\prod_{n=1}^{k}{\left( 1+\frac{x^{2n+1}}{n(n+1)}\right)}$$
  • $$P=\prod_{n=2}^{k}{\left( 1+\frac{(-1)^{n}x^{2n-1}}{n^3-1}\right)}$$
  • Вычислить бесконечную сумму ряда с заданной точностью е (e>0).
  • $$\sum_{i=1}^{\infty}{\frac{1}{i^2}}$$
  • $$\sum_{i=1}^{\infty}{\frac{1}{3^i+4^i}}$$
  • $$\sum_{i=1}^{\infty}{\frac{(-1)^1}{(2i-1)!}}$$
  • $$\sum_{i=1}^{\infty}{\frac{(-1)^{i+1}}{3^{2i-1}}}$$
  • Вычислить и вывести на экран значение функции $$F(x) $$ на отрезке $$[a,b]$$ c шагом $$h=0.1$$ с точностью $$e$$.Результат работы программы представить в виде следующей таблицы:
  • Значение x Значение функции F(x) Количество просуммированных слагаемых n
    1
    2
    Замечание. При решении задачи использовать вспомогательную функцию.
  • $$F(x) = 1+\frac{x^2}{4}+\frac{x^3}{4^2}+\frac{x^4}{4^3}+\frac{x^5}{4^4}+…, x\in [0,1;0,9]$$
  • $$F(x) = 1+\frac{x^3}{3\cdot 2}+\frac{x^5}{5\cdot 2^2}+\frac{x^7}{7\cdot 2^3}…, x\in [0,1;0,99]$$
  • $$F(x) = 1-\frac{x^2}{3!}+\frac{x^4}{5!}-\frac{x^6}{7!}+ \frac{x^8}{9!}…, x\in [0;1]$$
  • $$F(x) = \frac{x-1}{x}+\frac{(x-1)^2}{2x^2}+\frac{(x-1)^3}{3x^3}+…, x\in [1;2]$$
  • Вернуться к учебному плану