LEARN X · ЗА 17 МИН

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;
Поддержать проект