Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 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; просмотров: 261; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.217.21 (0.007 с.) |