1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 77
Текст из файла (страница 77)
Эта программа вычисляет и выводит значение факториала для ряда значений.Факториал числа N равен N*(N-l)*(N-2)*... *1. Например, факториал 4 равен4*3*2*1 = 24. Функция вычисления факториала работает только для положительных целых чисел. Это банальный программистский пример для иллюстрации ситуации, когда требуется обработка ошибок.// FactorialWithError - пример функции вычисления// факториала, в которой отсутствует проверка ошибокusing System;namespace FactorialWithError{// MyMathFunctions - набор созданных мною математических// функцийpublic class MyMathFunctions{// Factorial - возвращает факториал переданного// аргументаpublic static double Factorial(double dValue){// Начинаем со значения аккумулятора, равного 1double dFactorial = 1.0;// Цикл со счетчиком nValue, уменьшающимся до 1, с// умножением на каждой итерации значения аккумулятора// на величину счетчикаdo{dFactorial *= dValue;dValue -= 1.0;} while(dValue > 1 ) ;// Возвращаем вычисленное значениеreturn dFactorial;}}public class Program{public static void Main(string[] args){// Вызов функции вычисления факториала в// цикле от 6 до -6for (int i = 6; i > -6; I--){// Вывод результата на каждой итерацииConsole.WriteLine("i = { о } , факториал = {l}",i, MyMathFunctions.Factorial(i));}}}}// Ожидаем подтверждения пользователяConsole.WriteLine("Нажмите <Enter> для " +"завершения программы..
. ") ;Console.Read();Функция Factorial () начинается с инициализации переменной-аккумулятора значением 1. Затем функция входит в цикл, в котором на каждой итерации выполняется умножение на последовательно уменьшающееся значение счетчика nValue, пока nValue не достигнет 1. Накопленное в аккумуляторе значение возвращается вызывающей функции.Алгоритм Factorial () выглядит к о р р е к т н о — пока вы не начнете вызывать этуфункцию. Функция Main () также содержит цикл, в котором вычисляются значения факториала для ряда убывающих значений. Однако вместо того чтобы остановиться на значениизначений — до -6.394 1, функция Main () продолжает вычисления для отрицательныхЧасть VII.
ДополнительныеглавыВ результате на экране получается следующее:i = 6, факториал = 72 0i = 5, факториал = 12 0i = 4, факториал = 24i = 3 , факториал = 6i = 2, факториал = 21 = 1 , факториал = 1| = 0 , факториал = 0i = -1, факториал = -1i = -2, факториал = -2i = -3, факториал = -3i = -4, факториал = -4i - -5, факториал = -5Нажмите <Enter> для завершения программы...Как видите, часть результатов не имеет смысла. Во-первых, значение факториала неможет быть отрицательным. Во-вторых, обратите внимание, что отрицательные значениярастут совсем не так, как положительные. Понятно, что здесь присутствует ошибка.Если попытаться изменить цикл внутри Factorial () и записать его какdo{ .
. . }while (dValue ! =0), то программу при передаче отрицательногозначения просто ждет крах. Поэтому никогда не пишите такой оператор сравнений — while (dValue ! =0), поскольку ошибка приближения может в любом случае привести к неверному результату проверки на равенство 0.В особенности при работе с числами с плавающей точкой избегайте условийнаподобие dValue ! =0, в которых требуется точное сравнение для выхода изцикла. Используйте менее строгое условие, как, например, dValue>l.
Небольшая ошибка п р и б л и ж е н и я — такая как dValue = 0.00001— можетпривести к бесконечному циклу. Об ошибках приближения рассказываетсяв главе 3, "Объявление переменных-значений".Возврат индикатора ошибкиНесмотря на свою простоту, функция Factorial О требует проверки ошибочнойситуации: факториал отрицательного числа не определен. Функция Factorial ()должна включать проверку этого условия.Но что должна делать функция Factorial ( ) , столкнувшись с ошибкой? Лучшее,что она может сделать в такой ситуации — это сообщить об ошибке вызывающейфункции в надежде на то, что источник ошибки знает, почему она произошла и как сней справиться.Классический способ указать на происшедшую ошибку в функции — это возвратитьзначение, которое функция не в состоянии вернуть при безошибочной работе. Например,значение факториала не может быть отрицательным. Таким образом, факториал можетвозвращать значение - 1 , если ему передается отрицательный аргумент, -2 для нецелогоаргумента и так далее — для каждой ошибки некоторое соответствующее ей число.
Такие числа называются кодами ошибки. Вызывающая функция может проверить, не вернула ли вызываемая функция отрицательное значение, и если д а — то вызывающаяфункция будет знать о том, что произошла ошибка. Значение возвращаемого кода ошибки позволяет определить ее природу.Глава18.Эти исключительные исключения395jjjdWfeKУказанные изменения внесены в код демонстрационной программы Facto// FactorialErrorReturn - создание функции вычисления// факториала, которая возвращает код ошибки, если что-то// идет не такusing System;namespace FactorialErrorReturn{// MyMathFunctions - набор созданных мною математических// функцийpublic class MyMathFunctions{// Следующие коды ошибок представляют некорректные// значенияpublic const int NEGATIVE_NUMBER= -1;public const int NON_INTEGER_VALUE = -2;// Factorial - возвращает факториал переданного// аргументаpublic static double Factorial(double dValue){// Проверка: отрицательные значения запрещеныif (dValue < 0){return NEGATIVE NUMBER;}// Проверка: передано ли целое значение аргументаint nValue = (int)dValue;if (nValue != dValue){return NON INTEGER VALUE;}// Тесты пройдены, начинаем со значения аккумулятора,// равного 1double dFactorial = 1.0;// Цикл со счетчиком nValue, уменьшающимся до 1, с// умножением на каждой итерации значения аккумулятора// на величину счетчикаdo{dFactorial *= dValue;dValue -= 1.0;} while(dValue > 1 ) ;// Возвращаем вычисленное значениеreturn dFactorial;}}public class Program{public static void Main(stririg[] args)396 {Часть VII.
Дополнительные главы// Вызов функции вычисления факториала в// цикле от 6 до -6for (int i = 6; i > -6; i--){double dFactorial = MyMathFunctions.Factorial(i);if (dFactorial == MyMathFunctions.NEGATIVE NUMBER){Console.WriteLine("Factorial() получила отрицательный параметр");break;}if (dFactorial == MyMathFunctions.NON INTEGER VALUE){'"Console.WriteLine("Factorial() получила нецелый параметр");break;}// Вывод результата на каждой итерацииConsole.WriteLine("i = { о } , факториал = {l}",i, MyMathFunctions.Factorial(i));}// Ожидаем подтверждения пользователяConsole.WriteLine("Нажмите <Enter> для " +"завершения программы...");Console.Read ();Теперь перед началом вычислений функция Factorial () выполняет ряд проверок.Первая проверка — не отрицателен ли переданный функции аргумент.
Обратите внима7ние, что значение 0 разрешено, поскольку приводит к разумному результату . Если проверка не пройдена, функция тут же возвращает код ошибки. Затем выполняется второйтест, проверяющий, равен ли переданный аргумент своей целочисленной версии. Еслида — дробная часть аргумента равна 0.Функция Main () проверяет результат, возвращаемый функцией Factorial ( ) , напредмет обнаружения ошибок. Однако значения наподобие -1 и -2 мало информативныдля программиста, так что класс MyMathFunctions определяет пару целочисленныхконстант.
Константа NEGATIVE_NUMBER равна - 1 , a NON_INTEGER_VALUE2. Этоничего не меняет, но делает программу, в особенности функцию Main ( ) , существенноболее удобочитаемой.Обычно по соглашению для имен констант используются строчные буквы,а слова в имени разделены символами подчеркивания.Обращение к этим константам выполняется посредством имени класса, какMyMathClass. NEGATIVE_NUMBER. Константные переменные автоматически являются статическими, что делает их свойствами класса, разделяемымивсеми объектами. Другие варианты работы с константами описаны во врезке"Немного о константах".7Этот "разумный" результат некорректен, так как в математике принято, что факториал 0 равен 1.
— Примеч. ред.Глава18.Эти исключительные исключения397Немного о константахПредпочтительный способ записи констант почти всегда использует следую»щий, более гибкий по сравнению с применением const, подход.public static readonly int NEGATIVE_NUMBER = -1;Значение const вычисляется во время компиляции и может быть инициализированотолько числом или строкой. Статическая переменная только для чтения вычисляется вовремя выполнения программы и может быть инициализирована объектом любого вида,Используйте const только там, где производительность программы сверхкритична.Еще один способ определения констант — в данном случае группы связанных конс т а н т — п о с р е д с т в о м ключевого слова enum, как описано в главе 15, "Обобщенноепрограммирование".