Розвиток мов та підходів до програмування

Лекція 3. Розвиток мов та підходів до програмування. Технологія програмування

  • 3.1. Розвиток мов та підходів до програмування
  • 3.2. Технологія структурного програмування
    • 3.2.1. Поняття структурного програмування
    • 3.2.2. Методи структурного програмування
    • 3.2.3. Модульне програмування
  • 3.3. Технологія об'єктно-орієнтованого програмування
    • 3.3.1. Об'єктно-орієнтоване програмування
    • 3.3.2. Основні поняття об'єктно-орієнтованого програмування
    • 3.3.3. Принципи об'єктно-орієнтованого програмування

Перші мови програмування виникли відносно недавно. Як і слід було очікувати, перші мови програмування, як і перші ЕОМ, були досить примітивні й орієнтовані на чисельні розрахунки. Це були і суто теоретичні наукові розрахунки (перш за все, математичні та фізичні), і прикладні завдання, зокрема, у галузі військової справи.

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

Потрібно відзначити, що ранні мови програмування були оптимізовані під апаратну архітектуру конкретного комп'ютера, для якого призначалися, і, хоча вони забезпечували високу ефективність обчислень, до стандартизації було ще далеко. Програма, яка була цілком працездатною на одній обчислювальній машині, часто не могла виконуватися на іншій.

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

Друга половина ХХ століття ознаменувалася появою мов програмування так званого "високого рівня", порівняно з раніше розглянутими попередниками, відповідно іменованими низькорівневими мовами.

При цьому різниця полягає у підвищенні ефективності праці розробників за рахунок абстрагування від конкретних деталей апаратного забезпечення. Одна інструкція (оператор) мови високого рівня відповідала послідовності з декількох низькорівневих інструкцій, або команд. Виходячи з того, що програма, по суті, являла собою набір директив, звернених до комп'ютера, такий підхід до програмування отримав назву імперативного.

Ще однією особливістю мов високого рівня була можливість повторного використання раніше написаних програмних блоків, що виконують ті або інші дії, за допомогою їхньої ідентифікації та подальшого звернення до них, наприклад, за ім'ям. Такі блоки отримали назву функцій або процедур, і програмування набуло більш впорядкованого характеру.

Крім того, з появою мов високого рівня залежність реалізації від апаратного забезпечення істотно зменшилася. Платою за це стала поява спеціалізованих програм, які перетворюють інструкції мов у коди тієї або іншої машини, або трансляторів, а також деяка втрата у швидкості обчислень, яка, втім, компенсувалася істотним виграшем у швидкості розробки додатків і уніфікацією програмного коду.

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

Природно, що для навчання новим мовам програмування було потрібно багато часу і коштів, а ефективність реалізації на колишньому апаратному забезпеченні знижувалася. Проте це були тимчасові труднощі, та, як показала практика програмування, багато перших мов високого рівня виявилися настільки вдало реалізованими, що активно використовуються й сьогодні.

Одним з таких прикладів є мова Fortran, яка реалізує обчислювальні алгоритми. Інший приклад - мова APL, що трансформувалася в BPL і потім в C. Основні конструкції останньої залишаються незмінними ось уже кілька десятиліть.

У 60-х роках ХХ століття виникає новий підхід до програмування, який і дотепер успішно конкурує з імперативним, а саме, декларативний підхід. Суть підходу полягає в тому, що програма являє собою не набір команд, а описання дій, які необхідно здійснити.

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

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

На початковому етапі розвитку декларативним мовам програмування було складно конкурувати з імперативними в силу об'єктивних труднощів ефективної реалізації трансляторів. Програми працювали повільніше, проте вони могли вирішувати більш абстрактні завдання з меншими трудовитратами. Зокрема, мову SML було розроблено як засіб доведення теорем.

Різні діалекти мови LISP (зокрема, Interlisp, Common Lisp, Scheme), виникли тому, що ядро й ідеологія цієї мови виявилися досить ефективними при реалізації символьної обробки (аналізі текстів).

Інші характерні приклади декларативних мов програмування: SML, Haskell, Prolog.

Одним із шляхів розвитку декларативного стилю програмування став функціональний підхід, що виник після створення мови LISP.

Відмінною особливістю даного підходу є те, що будь-яка програма, написана такою мовою, може інтерпретуватися як функція з одним або кількома аргументами. Такий підхід дає можливість прозорого моделювання тексту програм математичними засобами, а значить, є дуже цікавим з теоретичної точки зору.

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

Більш того, типи окремих функцій, які використовуються у функціональних мовах, можуть бути змінними. Таким чином забезпечується можливість обробки різнорідних даних (наприклад, впорядкування елементів списку за зростанням для цілих чисел, окремих символів і рядків) або поліморфізм.

Ще однією важливою перевагою реалізації мов функціонального програмування є автоматизований динамічний розподіл пам'яті комп'ютера для зберігання даних. При цьому програміст позбувається обов'язку контролювати дані, а при необхідності може запустити функцію "збирання сміття" - очищення пам'яті від тих даних, які більше не будуть потрібні програмі (зазвичай цей процес періодично ініціюється комп'ютером).

Таким чином, при створенні програм за допомогою функціональних мов програміст зосереджується на області досліджень (предметній області) і в меншій мірі піклується про рутинні операції (забезпечення правильного з точки зору комп'ютера представлення даних, "збирання сміття" тощо).

Оскільки функція є природним формалізмом для мов функціонального програмування, реалізація різних аспектів програмування, пов'язаних із функціями, істотно спрощується. Зокрема, стає прозорим написання рекурсивних функцій, тобто функцій, що викликають самих себе в якості аргументу. Крім того, природною стає і реалізація обробки рекурсивних структур даних (наприклад, списків - базових елементів, скажімо, для мов сімейства LISP, дерев та ін.).

Завдяки реалізації механізму зіставлення зі зразком, такі мови як ML та Haskell цілком застосовні для символьної обробки.

Зрозуміло, що мови функціонального програмування не позбавлені недоліків. Часто до них відносять нелінійну структуру програми і відносно невисоку ефективність реалізації. Однак перший недолік досить суб'єктивний, а другий успішно подолано сучасними реалізаціями, зокрема, низкою останніх трансляторів мови SML, включаючи і компілятор для середовища Microsoft.NET.

У 70-х роках минулого століття виникла ще одна гілка мов декларативного програмування, пов'язана з проектами у галузі штучного інтелекту, а саме мови логічного програмування.

Згідно логічного підходу до програмування, програма являє собою сукупність правил або логічних висловлювань. Крім того, у програмі допустимі логічні причинно-наслідкові зв'язки, зокрема, на основі операції імплікації.

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

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

Одним з недоліків логічного підходу в концептуальному плані є специфічність класу вирішуваних завдань.

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

Нелінійність структури програми є особливістю декларативного підходу і, строго кажучи, являє собою оригінальну особливість, а не об'єктивний недолік.

Як приклад мов логічного програмування можна привести Prolog (назва виникла від слів PROgramming in LOGic) і Mercury.

Важливим кроком на шляху до вдосконалення мов програмування стала поява об'єктно-орієнтованого підходу до програмування (ООП) та відповідного класу мов.

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

Безперечною перевагою даного підходу є концептуальна близькість до предметної області довільної структури та призначення. Механізм спадкоємства атрибутів і методів дозволяє будувати похідні поняття на основі базових і таким чином створювати модель якої завгодно складної предметної області з заданими властивостями.

Ще одною теоретично цікавою і практично важливою властивістю об'єктно-орієнтованого підходу є підтримка механізму обробки подій, які змінюють атрибути об'єктів і моделюють їхню взаємодію в предметній області.

Пересуваючись ієрархією класів від більш загальних понять предметної області до більш конкретних (або від більш складних - до більш простих) і навпаки, програміст отримує можливість змінювати ступінь абстрактності або конкретності погляду на модельований ним реальний світ.

Використання раніше розроблених (можливо, іншими колективами програмістів) бібліотек об'єктів і методів дозволяє значно заощадити трудовитрати при виробництві програмного забезпечення, особливо типового.

Об'єкти, класи і Метод можуть бути поліморфними, що робить реалізоване програмне забезпечення більш гнучким і універсальним.

Складність адекватної (несуперечливої і повної) формалізації об'єктної теорії породжує труднощі тестування та верифікації створеного програмного забезпечення. Ймовірно, ця обставина є одним з найбільш істотних недоліків об'єктно-орієнтованого підходу до програмування.

Мабуть, найбільш відомим прикладом об'єктно-орієнтованої мови програмування є мова C++, яка розвинулася з імперативної мови С. Її прямим нащадком і логічним продовженням є мова С#. Інші приклади об'єктно-орієнтованих мов програмування: Visual Basic, Eiffel, Oberon.

Розвитком концепції керованої подіями об'єктно-орієнтованого підходу стала поява у 90-х роках минулого столвття цілого класу мов програмування, які отримали назву мов сценаріїв або скриптів.

У рамках даного підходу програма являє собою сукупність можливих сценаріїв обробки даних, вибір яких ініціюється настанням тієї чи іншої події (клацання по кнопці миші, попадання курсора у певну позицію, зміна атрибутів того чи іншого об'єкта, переповнення буфера пам'яті тощо). Події можуть ініціюватися як операційною системою, так і користувачем.

Основні переваги мов даного класу успадковані від об'єктно-орієнтованих мов програмування. Це інтуїтивна ясність описів, близькість до предметної області, високий ступінь абстракції, добра переносимість.

Широкі можливості повторного використання коду також успадковані сценарними мовами від об'єктно-орієнтованих предків.

Істотною перевагою мов сценаріїв є їхня сумісність з передовими інструментальними засобами автоматизованого проектування і швидкої реалізації програмного забезпечення, або так званими CASE-(Computer-Aided Software Engineering) і RAD-(Rapid Application Development) засобами.

Одним з найбільш передових інструментальних комплексів, призначених для швидкої розробки додатків, є Microsoft Visual Studio.NET.

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

Характерні приклади сценарних мов програмування: VBScript, PowerScript, LotusScript, JavaScript.

Ще один дуже важливий клас мов програмування - мови підтримки паралельних обчислень.

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

Мови паралельних обчислень дозволяють досягти помітного виграшу при обробці великих масивів інформації, що надходять від одночасно працюючих користувачів, або маючих високу інтенсивність (як, наприклад, відеоінформація або звукові дані високої якості).

Іншою досить значимою областю застосування мов паралельних обчислень є системи реального часу, в яких користувачеві необхідно отримати відповідь від системи безпосередньо після запиту. Такого роду системи відповідають за життєзабезпечення і прийняття відповідальних рішень.

Зворотною стороною переваг розглянутого класу мов програмування є висока вартість розробки програмного забезпечення, отже, створення відносно невеликих програм широкого (наприклад, побутового) застосування часто нерентабельно.

Прикладами мов програмування з підтримкою паралельних обчислень можуть служити Ada, Modula-2 і Oz.

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