Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Learning OCaml, for C, C++, Perl and Java programmers by Richard W.M. Jones

Глава 4.

Перевод: И.Зенков

Содержание

  1. Нулевые значения
  2. Утверждения, предупреждения, фатальные ошибки и вывод на stderr

Нулевые значения

Предположим, что на своём сайте вы решили провести исследование, дабы узнать у читателей их возраст и имена. Существует только проблема в том, что по некоторым причинам, некоторые из ваших читатели не хотят указывать свой возраст, упрямо отказываясь заполнять соответствующую область. Что, в данном случае, должен делать бедный администратор базы данных?

Предположим, что возраст представлен как int, тогда у нас есть два возможных способа решить сложившуюся проблему. Наиболее обычный (и наиболее неправильный) способ заключается в принятии "некоторого" значения когда возраст не будет обработан. Предположим, когда возраст = -1, тогда информация не будет принята, в противном случае она будет прината (даже если в корне неверна!). Этот способ работает если вы, для примера, решили выяснить средний возраст посетителей своего сайта. Забыв учесть некоторые значения вы решаете, что средний возраст ваших посетителей 7½, наняв web дизайнеров для удаления длинных слов и подбора текущей цветовой схемы.

Другой, более корректный способ, состоит в том, чтобы хранить возраст в области которая имеет тип "int или null". Вот SQL таблица для хранения возрастов:

create table users
(
  userid serial,
  name text not null,
  age int             -- may be null
);

Если данные о возрасте не получены тогда в базу отправляется специальное SQL значение null. SQL автоматически его игнорирует когда вы запрашиваете вычислить среднее число или что-нибудь в таком духе.

В языках программирования тоже реализована поддержка нулевых значений, причём в одних эти значения легче использовать, чем в других. Например в Perl любой scalar (то есть число или строка) может быть обозначен как undef (способ определения нулевого значения в Perl). Это вызывает множество опасных моментов, которые часто игнорируются неопытными программистами, что может привести к серьёзным ошибкам. В Java любая ссылка на объект тоже может быть нулевой, потому в Java имеет смысл хранить возраст как Integer и позволить ссылкам на возраст быть null. В C значения тоже могут быть нулевыми, но если вы бы хотели, чтобы простое целое число было нулевым, вам необходимо в начале боксировать его в объект размещённый в malloc.

OCaml предлагает элегантное решение этой проблемы, используя простой вариант определения полиморфного типа:

type 'a option = None | Some of 'a

В данном случае нулевое значение обозначено как None. В примере выше, тип возраста (int которое может быть нулевым) является int option [Помните: выводы навроде int list и int binary_tree].

# Some 3;;
- : int option = Some 3

А, что на счёт списка дополнительных целых чисел?

# [ None; Some 3; Some 6; None ];;
- : int option list = [None; Some 3; Some 6; None]

А, как на счёт дополнительного списка целых чисел?

# Some [1; 2; 3];;
- : int list option = Some [1; 2; 3]

Утверждения, предупреждения, фатальные ошибки и вывод на stderr

Одна замечательная особенность Perl'a — богатый набор команд для отладки программ и обработки неожиданных ошибок, включая способность печатать стек трассировки, отсылать и ловить исключения (более подробно об исключениях мы поговорим позже), ну и так далее. OCaml имеет не на столько богатый набор отладочных команд то, что есть просто лучше чем Java, схоже с C и не столь хорошо как в Perl.

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

# assert (Sys.os_type = "Win32");;
Exception: Assert_failure ("", 0, 30).

Разумеется запуск этого кода на Win32 не вызовет ошибку.

Если в вашей программе, что-то пошло не так, вы можете использовать assert false для её остановки, но лучше всего для этой целей подойдёт...

failwith "error message" отправляет Failure исключение, которое не будучи поймано, остановит программу и выдаст вам "error message". failwith часто используется в процессе сравнения с образцом, как в этом примере:

  match Sys.os_type with
    "Unix" | "Cygwin" ->   (* code omitted *)
  | "Win32" ->             (* code omitted *)
  | "MacOS" ->             (* code omitted *)
  | _ -> failwith "this system is not supported"

Обратите внимание на две особенности сравнения с образцом, в этом примре. Так называемый "range pattern" используется для проверки соответствия "Unix" или "Cygwin", а специальный символ _ обозначает "что-нибудь ещё".

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

open Graphics;;

open_graph " 640x480";;
for i = 12 downto 1 do
  let radius = i * 20 in
  prerr_endline ("radius is " ^ (string_of_int radius));
  set_color (if (i mod 2) = 0 then red else yellow);
  fill_circle 320 240 radius
done;;
read_line ();;

Если вы предпочитаете printf в C-стиле, тогда используйте OCaml модуль Printf:

open Graphics;;
open Printf;;

open_graph " 640x480";;
for i = 12 downto 1 do
  let radius = i * 20 in
  printf "radius is %d\n" radius;x
  set_color (if (i mod 2) = 0 then red else yellow);
  fill_circle 320 240 radius
done;;
read_line ();;