Ada
Express-тур по Ada за 17 минут: типы, диапазоны, циклы, записи, пакеты, исключения и строгая типизация — весь язык в комментариях кода.
Ada — компилируемый язык со строгой статической типизацией, созданный для систем, где цена ошибки высока: авионика, железные дороги, медицинская и военная техника. Девиз языка — безопасность через явность: компилятор и проверки времени выполнения ловят то, что в других языках выстреливает уже в продакшене. Ниже — весь язык на одной странице, почти всё спрятано в комментариях кода.
Структура программы
Программа на Ada — это процедура, обрамлённая with/use сверху и begin/end в теле.
-- Это однострочный комментарий: начинается с двух дефисов и идёт до конца строки.
-- Многострочных комментариев в Ada НЕТ — каждую строку комментируют отдельно.
-- with подключает библиотечный пакет (как import).
with Ada.Text_IO;
-- use открывает имена пакета без префикса (можно писать Put_Line вместо Ada.Text_IO.Put_Line).
use Ada.Text_IO;
-- Главная процедура. Имя процедуры (Hello) обычно совпадает с именем файла (hello.adb).
procedure Hello is
-- Здесь, между is и begin, объявляют переменные и типы.
begin
-- Исполняемая часть — между begin и end.
Put_Line ("Привет, Ada!"); -- вывод строки с переводом строки
end Hello;
-- Ada нечувствительна к регистру: Put_Line, put_line и PUT_LINE — одно и то же.
-- Каждый оператор завершается точкой с запятой.
Переменные и типы
Объявление идёт в форме Имя : Тип := Значение;. Типизация строгая: смешивать Integer и Float без явного преобразования нельзя.
declare
-- Целое число.
Age : Integer := 25;
-- Число с плавающей точкой.
Pi : Float := 3.14159;
-- Логический тип: значения True и False.
Is_Ready : Boolean := True;
-- Один символ — в одинарных кавычках.
Grade : Character := 'A';
-- Константа: добавляем слово constant, переприсвоить нельзя.
Max : constant Integer := 100;
-- Без := значение не задано (мусор). Хороший тон — инициализировать.
Counter : Integer := 0;
begin
-- Строгая типизация: так НЕЛЬЗЯ — Pi := Age; (ошибка компиляции).
-- Нужно явное преобразование:
Pi := Float (Age); -- Integer -> Float
Counter := Integer (Pi); -- Float -> Integer (с округлением)
null; -- null; — пустой оператор «ничего не делать»
end;
Пользовательские типы
Сила Ada — в собственных типах с заданным диапазоном. Выход за границы ловится как ошибка.
declare
-- Новый числовой тип с ограниченным диапазоном.
type Temperature is range -50 .. 60;
-- Подтип — тот же тип, но с более узким диапазоном (совместим с базовым).
subtype Positive_Temp is Temperature range 0 .. 60;
-- Перечисление: набор именованных значений.
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
-- Тип-индекс для будущих массивов.
subtype Index is Integer range 1 .. 10;
T : Temperature := 20;
Today : Day := Wed;
begin
-- Присвоение вне диапазона вызовет исключение Constraint_Error во время выполнения:
-- T := 100; -- 100 > 60 — недопустимо!
-- Атрибуты перечислений:
Today := Day'Succ (Today); -- следующий: Wed -> Thu
Today := Day'First; -- первый элемент: Mon
null;
end;
Ввод-вывод
Базовый ввод-вывод живёт в Ada.Text_IO. Для чисел подключают специализированные пакеты.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; -- ввод-вывод целых
procedure IO_Demo is
Name : String (1 .. 20); -- строка фиксированной длины 20 символов
Last : Natural; -- сколько символов реально прочитано
N : Integer;
begin
Put ("Имя: "); -- Put — без перевода строки
Get_Line (Name, Last); -- читаем строку, Last := число введённых символов
Put ("Привет, ");
Put (Name (1 .. Last)); -- выводим только заполненную часть (срез)
New_Line; -- перевод строки
Put ("Число: ");
Get (N); -- читаем целое число
Put ("Квадрат = ");
Put (N * N); -- вывод целого через Integer_Text_IO
New_Line;
end IO_Demo;
Операторы и условия
Ветвление: if/elsif/else/end if и многовариантный case.
declare
N : Integer := 7;
Sign : Character;
Day_N : Integer := 3;
begin
-- Арифметика: + - * / mod rem ** (степень).
-- Сравнения: = /= < <= > >=
-- Логика: and or not and then (ленивое и) or else (ленивое или)
if N > 0 then
Sign := '+';
elsif N < 0 then -- именно elsif, не else if
Sign := '-';
else
Sign := '0';
end if; -- закрывается end if
-- case — выбор по значению. Все варианты должны быть покрыты.
case Day_N is
when 1 .. 5 => -- диапазон значений
Put_Line ("Будни");
when 6 | 7 => -- перечисление вариантов через |
Put_Line ("Выходные");
when others => -- others ловит всё остальное
Put_Line ("Неизвестно");
end case;
end;
Циклы
Есть базовый бесконечный loop, цикл while и for по диапазону. Выход — через exit.
declare
I : Integer := 0;
Sum : Integer := 0;
begin
-- 1) Бесконечный цикл с выходом по условию.
loop
I := I + 1;
exit when I > 5; -- exit when — выйти, когда условие истинно
end loop;
-- 2) Цикл while.
I := 0;
while I < 10 loop
I := I + 2;
end loop;
-- 3) Цикл for по диапазону. Переменная K объявляется автоматически.
for K in 1 .. 5 loop
Sum := Sum + K; -- 1+2+3+4+5 = 15
end loop;
-- for в обратном порядке.
for K in reverse 1 .. 3 loop
null; -- K = 3, 2, 1
end loop;
-- Именованный цикл и выход из вложенного по имени.
Outer : for A in 1 .. 3 loop
for B in 1 .. 3 loop
exit Outer when A * B > 4; -- выходим сразу из Outer
end loop;
end loop Outer;
end;
Массивы
Массив объявляется с явным типом индекса. Индексация — круглыми скобками.
declare
-- Тип массива: 5 целых, индексы 1..5.
type Int_Array is array (1 .. 5) of Integer;
Nums : Int_Array := (10, 20, 30, 40, 50);
-- Агрегат с указанием позиций и others.
Zeros : Int_Array := (others => 0); -- все нули
Mixed : Int_Array := (1 => 100, others => 7); -- первый 100, остальные 7
-- Индекс по перечислению.
type Day is (Mon, Tue, Wed);
type Hours is array (Day) of Integer;
Work : Hours := (Mon => 8, Tue => 8, Wed => 4);
X : Integer;
begin
X := Nums (1); -- доступ: первый элемент (10)
Nums (5) := 99; -- присваивание элементу
-- Атрибуты массива:
-- Nums'First = 1
-- Nums'Last = 5
-- Nums'Length = 5
-- Nums'Range = 1 .. 5 (удобно для for)
for I in Nums'Range loop
Nums (I) := Nums (I) + 1;
end loop;
end;
Записи
record — составной тип с именованными полями (аналог struct).
declare
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
-- Запись с тремя полями.
type Date is record
D : Integer range 1 .. 31;
M : Integer range 1 .. 12;
Y : Integer;
end record;
-- Запись со значениями полей по умолчанию.
type Point is record
X : Float := 0.0;
Y : Float := 0.0;
end record;
Today : Date := (D => 16, M => 6, Y => 2026); -- именованный агрегат
Origin : Point; -- оба поля = 0.0 по умолчанию
P : Point := (1.5, 2.5); -- позиционный агрегат
begin
Today.Y := 2027; -- доступ к полю через точку
P.X := P.X + Origin.X;
-- Записи можно копировать и сравнивать целиком.
Origin := P; -- копия всех полей
if Origin = P then -- сравнение всех полей
null;
end if;
end;
Процедуры и функции
Процедура не возвращает значение, функция возвращает. Параметры имеют режимы: in (чтение, по умолчанию), out (запись), in out (чтение и запись).
procedure Demo is
-- Функция: возвращает значение через return.
function Square (X : Integer) return Integer is
begin
return X * X;
end Square;
-- Процедура с режимами параметров.
-- in — только читаем (значение приходит снаружи)
-- out — только пишем (результат уходит наружу)
-- in out — и читаем, и меняем
procedure Swap (A, B : in out Integer) is
Tmp : Integer := A;
begin
A := B;
B := Tmp;
end Swap;
-- Параметр со значением по умолчанию.
procedure Greet (Name : String := "мир") is
begin
Put_Line ("Привет, " & Name); -- & — конкатенация строк
end Greet;
R, S : Integer := 0;
begin
R := Square (5); -- 25
S := 1; R := 2;
Swap (S, R); -- теперь S=2, R=1
Greet; -- "Привет, мир"
Greet (Name => "Ada"); -- передача по имени параметра
end Demo;
Пакеты
Пакет — единица модульности. Делится на спецификацию (что видно снаружи, файл .ads) и тело (реализация, файл .adb).
-- === Спецификация: файл math_utils.ads ===
package Math_Utils is
-- Объявления, видимые пользователям пакета.
Pi : constant Float := 3.14159;
function Add (A, B : Integer) return Integer;
function Circle_Area (R : Float) return Float;
private
-- В private — то, что скрыто от внешнего кода.
end Math_Utils;
-- === Тело: файл math_utils.adb ===
package body Math_Utils is
-- Реализации объявленных подпрограмм.
function Add (A, B : Integer) return Integer is
begin
return A + B;
end Add;
function Circle_Area (R : Float) return Float is
begin
return Pi * R * R;
end Circle_Area;
end Math_Utils;
-- === Использование ===
-- with Math_Utils;
-- ...
-- X := Math_Utils.Add (2, 3); -- с префиксом
-- (или use Math_Utils; и тогда просто Add (2, 3))
Обработка исключений
Исключения перехватываются блоком exception в конце begin/end. Свои исключения возбуждают через raise.
with Ada.Text_IO; use Ada.Text_IO;
procedure Exc_Demo is
-- Объявление собственного исключения.
Too_Big : exception;
function Check (X : Integer) return Integer is
begin
if X > 100 then
raise Too_Big; -- возбуждаем своё исключение
end if;
return X;
end Check;
A : Integer;
B : Integer := 10;
begin
A := 1 / 0; -- вызовет встроенное Constraint_Error
exception
-- Перехват конкретных исключений.
when Constraint_Error =>
Put_Line ("Деление на ноль или выход за диапазон");
when Too_Big =>
Put_Line ("Слишком большое значение");
when others => -- others — любые прочие исключения
Put_Line ("Что-то пошло не так");
end Exc_Demo;
-- Частые встроенные исключения:
-- Constraint_Error — нарушение диапазона/индекса, деление на ноль
-- Program_Error — ошибка логики выполнения
-- Storage_Error — кончилась память
Строгая типизация и безопасность
Главная идея Ada: чем больше проверок переложено на компилятор и времярантайма, тем меньше багов доживёт до эксплуатации.
declare
-- Два разных типа на основе целых — НЕ взаимозаменяемы.
type Meters is new Integer;
type Seconds is new Integer;
Distance : Meters := 100;
Time : Seconds := 10;
Bad : Meters;
begin
-- Компилятор не даст случайно сложить метры с секундами:
-- Bad := Distance + Time; -- ОШИБКА компиляции: разные типы!
-- Это и есть «сильная типизация»: единицы измерения не перепутаешь.
Bad := Distance + 5; -- а это ок: 5 — литерал типа Meters
-- Проверки диапазона работают в рантайме:
-- subtype Percent is Integer range 0 .. 100;
-- P : Percent := 150; -- Constraint_Error при выполнении
-- Атрибуты дают безопасные границы типов:
-- Integer'First, Integer'Last — минимум и максимум типа
-- Meters'Image (Distance) — текстовое представление значения
-- Принцип: «если компилируется — скорее всего, работает правильно».
-- Поэтому Ada выбирают там, где сбой стоит жизней или миллионов:
-- авионика, поезда, спутники, медтехника.
null;
end;