Імперативна мова#

Ada це багатопарадигмальна мова з підтримкою об’єктно-орієнтованого програмування та деяких елементів функціонального програмування, але її ядром є проста, узгоджена процедурна/імперативна мова, схожа на C або Pascal.

В інших мовах

Одна важлива відмінність між Ada та такою мовою, як C, полягає в тому, що оператори та вирази дуже чітко розрізняються. В Ada, якщо ви спробуєте використати вираз, де потрібен оператор, ваша програма не зможе скомпілюватися. Це правило підтримує корисний стилістичний принцип: вирази призначенні для отримання значень а не побічних ефектів. Це також може запобігти деяким помилкам, таким як помилкове використання оператора рівності = замість операції присвоювання := де потрібне саме присвоєння.

Hello world#

Ось дуже проста імперативна програма Ada:

    
    
    
        
with Ada.Text_IO; procedure Greet is begin -- Вивести "Hello, World!" на екран Ada.Text_IO.Put_Line ("Hello, World!"); end Greet;

яка може знаходитися в файлі greet.adb.

Якщо ви скомпілюєте цей файл за допомогою компілятора GNAT і запустите на виконання, ви отримаєте очікуваний результат.

$ gprbuild greet.adb
using project file [...]_default.gpr
Compile
   [Ada]          greet.adb
Bind
   [gprbind]      greet.bexch
   [Ada]          greet.ali
Link
   [link]         greet.adb

 $ ./greet
Hello, World!
 $

У наведеній вище програмі є кілька цікавих речей:

  • Підпрограма в Ada може бути або процедурою, або функцією. Процедура, як показано вище, не повертає значення під час виклику.

  • with використовується для посилання на зовнішні модулі, які потрібні процедурі. Це схоже на import у інших мовах або приблизно схоже на #include у C та C++. Пізніше ми побачимо, як вони працюють у деталях. Тут нам потрібен модуль стандартної бібліотеки, пакет Ada.Text_IO, який містить процедуру друку тексту на екрані: Put_Line.

  • Greet це процедура та основна точка входу для нашої першої програми. На відміну від C або C++, її можна назвати як завгодно. Точку входу визначить побудовник. У нашому простому прикладі gprbuild, побудовник GNAT, використовуватиме файл, який Ви передали як параметр.

  • Put_Line це процедура, як і Greet, за винятком того, що вона оголошена в модулі Ada.Text_IO. Це еквівалент С printf.

  • Коментарі починаються з -- і йдуть до кінця рядка. Синтаксису багаторядкового коментаря немає, тобто неможливо почати коментар в одному рядку з продовженням в наступному рядку. Єдиний спосіб створити кілька рядків коментарів в Ada, це використовувати -- для кожного рядка. Наприклад:

--  Ми починаємо коментар в цьому рядку
--  і продовжуємо в наступному

В інших мовах

Процедури подібні до функцій у C або C++, які повертають void. Пізніше ми побачимо, як оголошувати функції в Ada.

Ось мінімалістичний варіант прикладу "Hello, World":

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet is begin -- Вивести "Hello, World!" на екран Put_Line ("Hello, World!"); end Greet;

Ця версія використовує ключове слово use, яке має форму use ім'я-пакету. Як видно з виклику Put_Line, ефект полягає в тому, що на сутності з названого пакета можна посилатися безпосередньо, без необхідності вказувати ім'я-пакету. попереду.

Імперативна мова - If/Then/Else#

У цьому розділі описується оператор if і представлено кілька інших фундаментальних можливостей мови, включаючи цілочисельний ввід/вивід, оголошення змінних і режими доступу до параметрів підпрограм.

Оператор if не дивує за формою та функціями:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Check_Positive is N : Integer; begin -- Виводимо запит на екран Put ("Enter an integer value: "); -- Зчитуємо цілочисленне значення Get (N); if N > 0 then -- Виводимо число Put (N); Put_Line (" is a positive number"); end if; end Check_Positive;

Оператор if складається як мінімум із зарезервованого слова if, умови (яка має бути логічним значенням), зарезервованого слова then і непорожньої послідовності операторів (частина then) яка виконується, якщо умова оцінюється як Істина і завершальне end if.

У цьому прикладі оголошується цілочисленна змінна N, запитується у користувача ціле число, перевіряється, чи значення є додатним, і, якщо так, відображається ціле значення, за яким слідує рядок "це додатне число". Якщо значення відємне, процедура не відображає жодних результатів.

Тип Integer є попередньо визначеним типом зі знаком, і його діапазон залежить від архітектури комп’ютера. На типових сучасних процесорах ціле число має 32-розрядний знак.

Приклад ілюструє деякі базові функції для цілочисельного введення-виведення. Відповідні підпрограми знаходяться у пакеті Ada.Integer_Text_IO і включають процедуру Get (яка читає число з клавіатури) і процедуру Put (яка відображає ціле число).

Далі невеликі модифікації прикладу, які ілюструють оператор if із частиною else:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Check_Positive is N : Integer; begin -- Виводимо запит на екран Put ("Enter an integer value: "); -- Зчитуємо цілочисленне значення Get (N); -- Виводимо число Put (N); if N > 0 then Put_Line (" is a positive number"); else Put_Line (" is not a positive number"); end if; end Check_Positive;

У цьому прикладі, якщо вхідне значення є відємним, програма відображає значення, за яким слідує рядок "не є додатним числом".

Останній варіант ілюструє оператор if з декількома elsif:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Check_Direction is N : Integer; begin Put ("Enter an integer value: "); Get (N); Put (N); if N = 0 or N = 360 then Put_Line (" is due north"); elsif N in 1 .. 89 then Put_Line (" is in the northeast quadrant"); elsif N = 90 then Put_Line (" is due east"); elsif N in 91 .. 179 then Put_Line (" is in the southeast quadrant"); elsif N = 180 then Put_Line (" is due south"); elsif N in 181 .. 269 then Put_Line (" is in the southwest quadrant"); elsif N = 270 then Put_Line (" is due west"); elsif N in 271 .. 359 then Put_Line (" is in the northwest quadrant"); else Put_Line (" is not in the range 0..360"); end if; end Check_Direction;

У цьому прикладі очікується, що користувач введе ціле число від 0 до 360 включно, і відображається, якому квадранту чи осі відповідає значення. Оператор in перевіряє, чи знаходиться скалярне значення в заданому діапазоні, і повертає логічний результат. Ефект від програми має бути зрозумілим; пізніше ми побачимо альтернативний і ефективніший стиль для досягнення того самого ефекту за допомогою оператора case.

Ключове слово elsif відрізняється від C або C++, де замість нього використовуються вкладені блоки else .. if. І ще одна відмінність полягає в наявності end if, що дозволяє уникнути проблеми, відомої як «висячий else».

Імперативна мова - Цикли#

Ada має три способи визначення циклів. Вони відрізняються від циклів C / Java / Javascript, проте більш простим синтаксисом і семантикою відповідно до філософії Ada.

Цикли For#

Першим типом циклу є цикл for, який дозволяє виконувати ітерацію в дискретному діапазоні.

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet_5a is begin for I in 1 .. 5 loop -- Виклик процедури Put_Line Put_Line ("Hello, World!" & Integer'Image (I)); -- ^ Procedure parameter end loop; end Greet_5a;

Виконання дає такий результат:

Hello, World! 1
Hello, World! 2
Hello, World! 3
Hello, World! 4
Hello, World! 5

Кілька речей, на які варто звернути увагу:

  • 1 .. 5 це дискретний діапазон від 1 до 5 включно.

  • Параметр циклу I (назва довільна) у тілі циклу має значення в цьому діапазоні.

  • I є локальним для циклу, тому ви не можете посилатися на I поза циклом.

  • Хоча значення I збільшується на кожній ітерації, з точки зору коду всередині циклу воно є постійним. Змінювати його неможна. Спроба змінити його значення призведе до помилки компіляції.

  • Integer'Image це функція, яка приймає ціле число та перетворює його на String. Це приклад мовної конструкції, відомої як атрибут, позначений синтаксисом ', який буде розглянуто більш детально пізніше.

  • Символ & є оператором конкатенації для строкових значень

  • end loop позначає кінець циклу

«Крок» циклу обмежений 1 (напрямок вперед) і -1 (назад). Щоб виконати ітерацію в діапазоні назад, використовуйте ключове слово reverse:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet_5a_Reverse is begin for I in reverse 1 .. 5 loop Put_Line ("Hello, World!" & Integer'Image (I)); end loop; end Greet_5a_Reverse;

Виконання дає такий результат:

Hello, World! 5
Hello, World! 4
Hello, World! 3
Hello, World! 2
Hello, World! 1

Межі циклу for можуть бути обчислені під час виконання; вони обчислюються один раз перед виконанням тіла циклу. Якщо значення верхньої межі менше значення нижньої, то цикл не виконується взагалі. Це також стосується циклів reverse. Таким чином, у наступному прикладі виводу на екран не буде:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet_No_Op is begin for I in reverse 5 .. 1 loop Put_Line ("Hello, World!" & Integer'Image (I)); end loop; end Greet_No_Op;

Докладніше про цикл for буде підніше.

Безумовний цикл#

Найпростішим циклом в Ada є безумовний цикл, який є основою для інших типів циклів Ada.

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet_5b is -- Оголошуємо змінну: I : Integer := 1; -- ^ Тип -- ^ Начальне значення begin loop Put_Line ("Hello, World!" & Integer'Image (I)); -- Вихід із циклу: exit when I = 5; -- ^ Умова -- Присвоєння: I := I + 1; -- Конструкції як в С 'I++' немає end loop; end Greet_5b;

Цей приклад дає той самий ефект, що й Greet_5a, показаний раніше.

Він ілюструє кілька концепцій:

  • Ми оголосили змінну з назвою I між is і begin. Це являє собою декларативний блок. Ada чітко відокремлює декларативну область від суто коду підпрограми. Оголошення може відбуватися в декларативній області, але не допускається посеред коду.

  • Цикл починається ключовим словом loop і, як і будь-який тип циклу, завершується комбінацією ключових слів end loop. Сам по собі це нескінченний цикл. Ви можете вийти з цього за допомогою оператора exit

  • Синтаксис присвоєння: :=, а рівності — =. Немає способу сплутати їх, оскільки, як зазначалося раніше, в Ada твердження та вирази є різними, а вирази не є дійсними твердженнями.

Цикл While#

Останній вид циклу в Ada — це цикл while.

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet_5c is I : Integer := 1; begin -- Умова має бути логічним виразом -- (не численним). -- Оператор "<=" вертає результат -- порівняння while I <= 5 loop Put_Line ("Hello, World!" & Integer'Image (I)); I := I + 1; end loop; end Greet_5c;

Умова оцінюється перед кожною ітерацією. Якщо результат хибний, то цикл припиняється.

Ця програма робить то самє, що й попередні.

В інших мовах

Зауважте, що Ada має іншу семантику, ніж мови на основі C щодо умови в циклі while. В Ada умова має бути логічним значенням, інакше компілятор відхилить програму; умова не є цілим числом, яке розглядається як True або False залежно від того, відмінне воно від нуля чи ні.

Імперативна мова - Case#

Оператор Ada case схожий на оператор C і C++ switch, але з деякими важливими відмінностями.

Ось приклад, варіація програми, яка була показана раніше з оператором if:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Check_Direction is N : Integer; begin loop Put ("Enter an integer value: "); Get (N); Put (N); case N is when 0 | 360 => Put_Line (" is due north"); when 1 .. 89 => Put_Line (" is in the northeast quadrant"); when 90 => Put_Line (" is due east"); when 91 .. 179 => Put_Line (" is in the southeast quadrant"); when 180 => Put_Line (" is due south"); when 181 .. 269 => Put_Line (" is in the southwest quadrant"); when 270 => Put_Line (" is due west"); when 271 .. 359 => Put_Line (" is in the northwest quadrant"); when others => Put_Line (" Au revoir"); exit; end case; end loop; end Check_Direction;

Ця програма постійно запитує ціле число, а потім, якщо значення знаходиться в діапазоні 0 .. 360, відображає відповідний квадрант або вісь. Якщо значення виходить за межі цього діапазону, цикл (і програма) припиняється після виведення прощального повідомлення.

Ефект оператора case подібний до оператора if у попередньому прикладі, але оператор case може бути ефективнішим, оскільки він не передбачає багаторазових перевірок діапазону.

Важливі моменти щодо конструкції case:

  • Вираз для case (тут змінна N) має бути дискретного типу, тобто або цілого типу, або типу перерахування. Дискретні типи будуть розглянуті більш детально пізніше дискретні типи.

  • Кожне можливе значення виразу case має бути охоплено одною з гілок оператора case. Це буде перевірено під час компіляції.

  • Гілка може охоплювати одне значення, наприклад 0; діапазон значень, наприклад 1 .. 89; або будь-яка комбінація значень (розділених символом |).

  • Як особливий випадок, необов’язкова кінцева гілка може вказати others, яка охоплює всі інші значення, не включені в попередні гілки.

  • Виконання складається з оцінки виразу case, а потім передачі керування коду в унікальній гілці, яка охоплює це значення.

  • Коли виконання коду у вибраній гілці завершено, керування передається коду після end case. На відміну від C, виконання не переходить до наступної гілки. Отже, Ada не потребує (і не має) оператора break.

Імперативна мова - Декларативні блоки#

Як згадувалося раніше, Ada проводить чіткий синтаксичний розподіл між деклараціями, які вводять імена для сутностей, які використовуватимуться в програмі, та операторами, які виконують обробку. Області в програмі, де можуть з’являтися декларації, називаються декларативними блоками.

У будь-якій підпрограмі розділ між is і begin є декларативним блоком. Ви можете оголосити там змінні, константи, типи, внутрішні підпрограми та інші сутності.

Ми коротко згадували про оголошення змінних у попередньому підрозділі. Давайте розглянемо простий приклад, де ми оголошуємо цілочисельну змінну X у декларативному блоці та виконуємо її ініціалізацію та модифікацію:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Main is X : Integer; begin X := 0; Put_Line ("The initial value of X is " & Integer'Image (X)); Put_Line ("Performing operation on X..."); X := X + 1; Put_Line ("The value of X now is " & Integer'Image (X)); end Main;

Давайте розглянемо ще один приклад:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Main is procedure Nested is begin Put_Line ("Hello World"); end Nested; begin Nested; -- Викликали процедуру Nested end Main;

Оголошення неможливі посеред операторів. Якщо вам потрібно оголосити локальну змінну серед операторів, ви можете оголосити новий декларативний блок за допомогою оператора declare:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Greet is begin loop Put_Line ("Please enter your name: "); declare Name : String := Get_Line; -- ^ Виклик функції -- Get_Line begin exit when Name = ""; Put_Line ("Hi " & Name & "!"); end; -- Змінної Name більше не існує end loop; Put_Line ("Bye!"); end Greet;

Увага

Функція Get_Line дозволяє вам отримувати дані від користувача та отримати строку як результат. Це більш-менш еквівалентно функції C:c:scanf.

Вона повертає String, який, як ми побачимо пізніше, є Необмеженим масивом. Наразі ми просто зауважимо, що якщо ви бажаєте оголосити змінну String і не знаєте її розмір заздалегідь, вам потрібно ініціалізувати змінну під час її оголошення.

Імперативна мова - умовні вирази#

Ada 2012 додала аналог виразу для умовних операторів (if і case).

If вираз#

Ось альтернативна версія прикладу, який ми бачили раніше; оператор if було замінено виразом if:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Check_Positive is N : Integer; begin Put ("Enter an integer value: "); Get (N); Put (N); declare S : constant String := (if N > 0 then " is a positive number" else " is not a positive number"); begin Put_Line (S); end; end Check_Positive;

Вираз if використовує один із двох строк залежно від N і присвоює це значення локальній змінній S.

Вирази Ada if подібні до операторів if. Однак є кілька відмінностей, які пов’язані з тим, що це вираз:

  • Обидва варіанти мають бути одного типу

  • Він має бути в дужках, якщо навколишній вираз їх ще не містить

  • Гілка else є обов’язковою, якщо вираз після then не є логічним типом. У цьому випадку гілка else є необов’язковою і, якщо її немає, за замовчуванням використовується else True.

Ось інший приклад:

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Main is begin for I in 1 .. 10 loop Put_Line (if I mod 2 = 0 then "Even" else "Odd"); end loop; end Main;

Ця програма виводить 10 рядків, чергуючи «Непарні» та «Парні».

Case вираз#

Подібно до виразів if, Ada також має вирази case. Вони працюють так, як Ви очікуєте.

    
    
    
        
with Ada.Text_IO; use Ada.Text_IO; procedure Main is begin for I in 1 .. 10 loop Put_Line (case I is when 1 | 3 | 5 | 7 | 9 => "Odd", when 2 | 4 | 6 | 8 | 10 => "Even"); end loop; end Main;

Ця програма робить то самє, що й попередній приклад.

Синтаксис відрізняється від оператора case, в даному випадку варіанти розділені комами.