Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Обработка исключений, возникших в чистом коде
В стандарте языка Haskell 98 года присутствует механизм обработки исключений ввода-вывода, который в настоящее время считается устаревшим. Согласно современному подходу все исключения, воз- никшие как при выполнении чистого кода, так и при осуществлении ввода-вывода, должны обрабатываться единообразно. Этой цели слу- жит единая иерархия типов исключений из модуля Control.Exception, в которую легко можно включать собственные типы исключений. Любой тип исключения должен реализовывать экземпляр класса ти- пов Exception. В модуле Control.Exception объявлено несколько конк- ретных типов исключений, среди которых IOException (исключения ввода-вывода), ArithException (арифметические ошибки, например, деление на ноль), ErrorCall (вызов функции error), PatternMatchFail
(не удалось выбрать подходящий образец в определении функции) и другие. Простейший способ выполнить действие, которое потенциаль- но может вызвать исключение,– воспользоваться функцией try: try:: Exception e => IO a -> IO (Either e a) Функция try пытается выполнить переданное ей действие вво- да-вывода и возвращает либо Right <результат действия> либо Left <исключение>, например: ghci> try (print $ 5 `div` 2):: IO (Either ArithException ()) 2 Right () ghci> try (print $ 5 `div` 0):: IO (Either ArithException ()) Left divide by zero Обратите внимание, что в данном случае потребовалось явно указать тип выражения, поскольку для вывода типа информации недостаточно. Помимо прочего, указание типа исключения позво- ляет обрабатывать не все исключения, а только некоторые. В сле- дующем примере исключение функцией try обнаружено не будет: > try (print $ 5 `div` 0):: IO (Either IOException ()) *** Exception: divide by zero Указание типа SomeException позволяет обнаружить любое ис- ключение: ghci> try (print $ 5 `div` 0):: IO (Either SomeException ()) Left divide by zero Попробуем написать программу, которая принимает два числа в виде параметров командной строки, делит первое число на вто- рое и наоборот и выводит результаты. Нашей первой целью будет корректная обработка ошибки деления на ноль. import Control.Exception import System.Environment
printQuotients:: Integer -> Integer -> IO () printQuotients a b = do print $ a `div` b
print $ b `div` a
params:: [String] -> (Integer, Integer) params [a,b] = (read a, read b)
main = do args <- getArgs let (a, b) = params args res <- try (printQuotients a b):: IO (Either ArithException ()) case res of Left e -> putStrLn "Деление на 0!" Right () -> putStrLn "OK"
putStrLn "Конец программы" Погоняем программу на различных значениях: $./quotients 20 7 OK Конец программы $./quotients 0 7 Деление на 0! Конец программы $./quotients 7 0 Деление на 0! Конец программы Понятно, что пока эта программа неустойчива к другим видам ошибок. В частности, мы можем «забыть» передать параметры ко- мандной строки или передать их не в том количестве: $./quotients quotients: quotients.hs:10:1-31: Non-exhaustive patterns in function params $./quotients 2 3 4 quotients: quotients.hs:10:1-31: Non-exhaustive patterns in function params Это исключение генерируется при вызове функции params, если переданный ей список оказывается не двухэлементным. Можно так- же указать нечисловые параметры: $./quotients a b quotients: Prelude.read: no parse
Исключение здесь генерируется функцией read, которая не в со- стоянии преобразовать переданный ей параметр к числовому типу. Чтобы справиться с любыми возможными исключениями, выде- лим тело программы в отдельную функцию, оставив в функции main получение параметров командной строки и обработку исключений: mainAction:: [String] -> IO () mainAction args = do let (a, b) = params args printQuotients a b
main = do args <- getArgs res <- try (mainAction args):: IO (Either SomeException ()) case res of Left e -> putStrLn "Ошибка" Right () -> putStrLn "OK" putStrLn "Конец программы" Мы были вынуждены заменить тип исключения на SomeException и сделать сообщение об ошибке менее информативным, поскольку теперь неизвестно, исключение какого вида в данном случае про- изошло. $./quotients a b Ошибка Конец программы $./quotients Ошибка Конец программы Понятно, что в общем случае обработка исключения должна зависеть от её типа. Предположим, что у нас имеется несколько об- работчиков для исключений разных типов: handleArith:: ArithException -> IO () handleArith _ = putStrLn "Деление на 0!"
handleArgs:: PatternMatchFail -> IO () handleArgs _ = putStrLn "Неверное число параметров командной строки!"
handleOthers:: SomeException -> IO () handleOthers e = putStrLn $ "Неизвестное исключение: " ++ show e
К сожалению, чтобы увидеть исключение от функции read, нуж- но воспользоваться наиболее общим типом SomeException. Вместо того чтобы вручную вызывать функцию обработчика при анализе результата try, можно применить функцию catch, вот её тип:
ghci>:t catch catch:: Exception e => IO a -> (e -> IO a) -> IO a
ПРИМЕЧАНИЕ. Модуль Prelude экспортирует старую версию функции catch, которая способна обрабатывать только исключе- ния ввода-вывода. Чтобы использовать новый вариант её опре- деления, необходимо использовать скрывающий импорт: import Prelude hiding (catch). Функция catch принимает в качестве параметров действие и обработчик исключения: если при выполнении действия генери- руется исключение, то вызывается его обработчик. Тип обработ- чика определяет, какие именно исключения будут обработаны. Рассмотрим примеры, в которых функция mainAction вызывается непосредственно в GHCi: ghci> mainAction ["2","0"] *** Exception: divide by zero ghci> mainAction ["0","2"] `catch` handleArith 0 Деление на 0! ghci> mainAction ["2","0"] `catch` handleArgs *** Exception: divide by zero ghci> mainAction ["2","0"] `catch` handleOthers Неизвестное исключение: divide by zero ghci> mainAction ["a", "b"] `catch` handleArgs *** Exception: Prelude.read: no parse ghci> mainAction ["a", "b"] `catch` handleOthers Неизвестное исключение: Prelude.read: no parse Если строка, выводимая GHCi, начинается с ***, то соответс- твующее исключение не было обработано. Обратите внимание на обычный для функции catch инфиксный способ вызова. Заметьте также, что обработчик handleOthers способен обработать любое ис- ключение.
Вернёмся к основной программе. Нам хочется, чтобы возник- шее исключение было обработано наиболее подходящим образом: если произошло деление на ноль, то следует выполнить handleArith, при неверном числе параметров командной строки – handleArgs, в остальных случаях – handleOthers. В этом нам поможет функция catches, посмотрим на её тип: >:t catches catches:: IO a -> [Handler a] -> IO a Функция catches принимает в качестве параметров действие и список обработчиков (функций, которые упакованы конструк- тором данных Handler) и возвращает результат действия. Если в процессе выполнения происходит исключение, то вызывается пер- вый из подходящих по типу исключения обработчиков (поэтому, в частности, обработчик handleOthers должен быть последним). Пе- репишем функцию main так, чтобы корректно обрабатывались все возможные исключительные ситуации: main = do args <- getArgs mainAction args `catches` [Handler handleArith, Handler handleArgs, Handler handleOthers] putStrLn "Конец программы" Посмотрим, как она теперь работает: $./quotients 20 10 Конец программы $./quotients Неверное число параметров командной строки! Конец программы $./quotients 2 0 Деление на 0! Конец программы $./quotients a b Неизвестное исключение: Prelude.read: no parse Конец программы
В этом разделе мы разобрались с работой функций try, catch и catches, позволяющих обработать исключение, в том числе и воз- никшее в чистом коде. Заметьте ещё раз, что вся обработка выпол- нялась в рамках действий ввода-вывода. Посмотрим теперь, как работать с исключениями, которые возникают при выполнении операций ввода-вывода.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 160; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.17.184.90 (0.02 с.) |