Как стать автором
Обновить

Введение в OCaml: Нулевые указатели, утверждения и предупреждения [4]

Время на прочтение 4 мин
Количество просмотров 3.1K
Автор оригинала: ocaml-tutorial.org
[прим. пер.: предыдущие части [1; 2; 3]
Предыдущие части переводил amarao, в этой части пункт про нулевые указатели также переведен им]

Нулевые указатели


Допустим, вы создали опрос на сайте, который спрашивает у читателей их имя и возраст. Единственной проблемой является, что по некоторым причинам некоторые из ваших читателей не хотят сообщать свой возраст — они упорно отказываются заполнять соответствующее поле. Что делает в этом случае плохой администратор базы данных?

Полагая, что возраст является целым числом, есть два варианта решения проблемы. Самый частый (и самый неправильный) это задать особое значение как признак незаполненности поля. Например, возраст -1 означает, что данные не были получены, иначе данные получены (даже если неверны). Этот метод отлично работает до тех пор, пока вы не решите посчитать средний возраст посетителей. Так как вы забыли учесть ваше специальное значение, вы приходите к выводу, что средний возраст посетителей семь с половиной лет, вы нанимаете веб-дизайнера для удаления всех длинных слов и переделки дизайна с использованием ярких цветов.

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

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

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

OCaml использует элегантное решение проблемы null используя простой полиморфный вариантный тип, определённый (в Pervasives) следующим образом:

type 'a option = None | Some of 'a

«Нулевой указатель» записывается как None. Возраст из примера выше (целое, которое может быть null) имеет тип int option [Помните, читается в обратном направлении, как int list и 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 является богатый набор команд для отладки программ и обработки неожиданных ошибок, в том числе возможность выводить срез стека, бросать и ловить исключения и т.д. OCaml имеет не такой богатый набор отладочных команд — лучше чем Java, примерно такой же как C, не такой хороший как Perl. (Мы будем говорить об исключениях более подробно позже.)

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

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

(Запуск на Win32 естественно не выдает ошибки)

Вы также можете просто вызвать assert false чтобы остановить вашу программу, просто если дела идут плохо, но вам вероятно лучше использовать…

failwith "error message" бросит исключение Failure которое опять таки если не ловить остановит программу с выводом переданного сообщением об ошибке. 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, то использовать модуль Printf:
open Graphics;;
open Printf;;

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


[прим. пер. насчет ссылки на оригинал: я знаю что оригинал на ocaml-tutorial.org, но у меня он не открывается [напрямую; через кэш Google; через archive.org; через Tor] так что я указал ссылку на зеркало]
Теги:
Хабы:
+4
Комментарии 1
Комментарии Комментарии 1

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн