Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[14]

С помощью конструкций exception и raise мы можем определять функции, которые сигнализируют о возникновении нежелательных ситуаций путем возбуждения исключений. Но, с другой стороны, нужны еще и какие-то средства для обработки этих исключений. Такая возможность, естественно, есть в ML; соответствующая конструкция называется обработчиком исключений (или просто обработчиком). Мы проиллюстрируем ее использование на следующем простом примере:

-fun head2 1st = head(lst) handle Head => 0;

>val head = fn : int list -> int

-head2([l,2,3]);

>1 : int

-head2(nil);

>0 : int

Выражение вида e handle exn => в вычисляется следующим способом: сначала вычисляется в; если результатом является некоторое значение v, то значением всего выражения является v; если в процессе вычисления в возбуждается исключение вхщ то вычисляется выражение в, и его значение будет значением всего выражения; если же возбуждается какое-либо другое исключение, то и все выражение возбуждает это исключение. Заметьте, что типы выражений в и в должны совпадать - иначе тип всего выражения зависел бы от того, возбудилось ли в пров

функции head2 есть int list -> int, хотя из анализа аргумента 1st никак не следует, что этот список является списком целых чисел. В приведенном выше примере head2 пытается применить head к 1st; если эта попытка завершается успешно, т.е. если head возвращает значение, то это значение и становится значением head2; иначе, т.е. если в процессе вычисления head (1st) возбуждается исключение Head, в качестве результата возвращается 0.

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

-exception Odd;

>exception Odd

-fun foo n = if n mod 2 <> 0 then

raise Odd

> val - fun

foo = bar m

else 17 div n; fn : int -> int

= foo(m) handle Odd I Div

>9999;

> val - foo

bar = 0;

fn : int -> int


Failure: Div

-bar 0;

>9999 : int; -foo 3; Failure: Odd

-bar 3;

>0 : int

-foo 20;

>1 : int

-bar 20;

>1 : int

При вычислении функции foo может произойти два исключительных события: деление на 0, в результате чего возбуждается исключение Div, или передача ей нечетного аргумента, в результате чего возбуждается исключена Odd. Функция bar обрабатывает оба этих исключения: если при вычислении foo(m) возбуждается исключение Odd, то bar(m) возвращает 0; если при вычислении foo(m) возбуждается исключение Div. то bar(m) возвращает 9999; в противном случае bar(m) возвращает значение fоо(m).

Обратите внимание на то, что синтаксис обработчика нескольких исключений очень похож на синтаксис определения безымянной функции с помощью набора правил. Действительно, можно рассматривать обработчик исключений как безымянную функцию, тип области определения которой есть ехп (тип исключительных значений), а тип области значений которой есть тип выражения слева от обработчика. С точки зрения проверки соответствия типов идентификаторы исключений суть не что иное, как конструкторы исключительных значений типа ехп - точно так же, как nil и : : являются конструкторами типа a list.

Более того, исключения могут содержать внутри себя другие значения - для этого достаточно просто объявить их с аргументом подходящего типа. Присоединенное к исключению значение может быть использовано обработчиком исключений. Следующий пример иллюстрирует эту возможность:

-exception oddlist of int list and oddstring of string;

>exception oddlist of int list exception oddstring of string

-... handle oddlist(nil) => 0

I oddlist(h::t) => 17

I oddstringC") => 0

I oddstring(s) => size(s)-l

Здесь объявление exception вводит два исключения: oddlist с аргументом типа int list, и oddstring с аргументом типа string. Обработчик


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

Что произойдет, если обозначенное в примере выше многоточием выражение возбудит исключение, отличное от oddlist и oddstring? Здесь аналогия с функциями заканчивается: в случае функции, если аргумент не отождествится ни с одним из образцов, возбуждается исключение Match; в случае же обработчика исключений, если встречается исключение, не предусмотренное в обработчике, это исключение возбуждается снова - в надежде, что какой-либо объемлющий обработчик исключений все же обработает его. Например:

-exception Theirs and Mine;

>exception Theirs exception Mine

-fun f(x) = if x=0 then raise Mine else raise Theirs;

>val f = fn : int -> a

-f(0) handle Mine => 7;

>7 : int

-f(l) handle Mine => 7; Failure: Theirs

-(f(l) handle Mine => 7) handle Theirs => 8;

>8 : int

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

-fun f(x) = raise (if x=0 then Mine else Theirs);

>val f = fn : int -> a

Как и при определении функции, в образце в обработчике исключений могут быть использованы универсальные образцы (или джокеры), которые могут быть сопоставлены с любым исключением. Например, следующее выражение перехватывает любые исключения, и, в этом случае, вырабатывает 0:

... handle => 0;

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



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32]