В информатике императивное программирование — это парадигма программирования программного обеспечения , которая использует операторы , изменяющие состояние программы . Во многом так же, как повелительное наклонение в естественных языках выражает команды, императивная программа состоит из команд, которые должен выполнить компьютер . Императивное программирование фокусируется на описании того, как программа работает шаг за шагом, [1], а не на высокоуровневых описаниях ее ожидаемых результатов.
Этот термин часто используется в противопоставлении декларативному программированию , которое фокусируется на том, что должна выполнить программа, не уточняя всех деталей того, как программа должна достичь результата. [2]
Процедурное программирование — это тип императивного программирования, в котором программа строится из одной или нескольких процедур (также называемых подпрограммами или функциями). Эти термины часто используются как синонимы, но использование процедур оказывает драматическое влияние на то, как выглядят императивные программы и как они строятся. Тяжелое процедурное программирование, в котором изменения состояния локализуются в процедурах или ограничиваются явными аргументами и возвратами из процедур, является формой структурного программирования . С 1960-х годов структурное программирование и модульное программирование в целом продвигались как методы улучшения сопровождаемости и общего качества императивных программ. Концепции, лежащие в основе объектно-ориентированного программирования, пытаются расширить этот подход.
Процедурное программирование можно считать шагом к декларативному программированию. Программист часто может сказать, просто взглянув на имена, аргументы и возвращаемые типы процедур (и соответствующие комментарии), что должна делать конкретная процедура, не обязательно вникая в детали того, как она достигает своего результата. В то же время полная программа по-прежнему необходима, поскольку она фиксирует операторы, которые должны быть выполнены, и порядок их выполнения в значительной степени.
Парадигма программирования, используемая для создания программ почти для всех компьютеров, обычно следует императивной модели. [примечание 1] Аппаратное обеспечение цифровых компьютеров предназначено для выполнения машинного кода , который является родным для компьютера и обычно написан в императивном стиле, хотя для некоторых архитектур, таких как машины LISP, существуют низкоуровневые компиляторы и интерпретаторы, использующие другие парадигмы .
С этой низкоуровневой точки зрения состояние программы определяется содержимым памяти, а операторы являются инструкциями на родном машинном языке компьютера. Императивные языки более высокого уровня используют переменные и более сложные операторы, но все равно следуют той же парадигме. Рецепты и контрольные списки процессов , хотя и не являются компьютерными программами , также являются знакомыми концепциями, которые по стилю похожи на императивное программирование; каждый шаг является инструкцией, а физический мир хранит состояние. Поскольку основные идеи императивного программирования как концептуально знакомы, так и напрямую воплощены в оборудовании, большинство компьютерных языков написаны в императивном стиле.
Операторы присваивания в императивной парадигме выполняют операцию над информацией, находящейся в памяти, и сохраняют результаты в памяти для последующего использования. Высокоуровневые императивные языки, кроме того, позволяют вычислять сложные выражения , которые могут состоять из комбинации арифметических операций и оценок функций , и присваивать результирующее значение памяти. Операторы циклов (например, циклы while , циклы do while и циклы for ) позволяют выполнять последовательность операторов несколько раз. Циклы могут либо выполнять содержащиеся в них операторы предопределенное количество раз, либо выполнять их повторно, пока не будет выполнено некоторое условие. Операторы условного ветвления позволяют выполнять последовательность операторов только при выполнении некоторого условия. В противном случае операторы пропускаются, и последовательность выполнения продолжается с оператора, следующего за ними. Операторы безусловного ветвления позволяют переносить последовательность выполнения в другую часть программы. К ним относятся переход (называемый goto во многих языках), switch и вызов подпрограммы, подпрограммы или процедуры (который обычно возвращает к следующему оператору после вызова).
На раннем этапе развития языков программирования высокого уровня введение блока позволило создавать программы, в которых группа операторов и деклараций могла рассматриваться как один оператор. Это, наряду с введением подпрограмм , позволило выразить сложные структуры путем иерархической декомпозиции в более простые процедурные структуры.
Многие императивные языки программирования (такие как Fortran , BASIC и C ) являются абстракциями языка ассемблера . [3]
Самые ранние императивные языки были машинными языками оригинальных компьютеров. В этих языках инструкции были очень простыми, что упрощало реализацию оборудования, но затрудняло создание сложных программ. FORTRAN , разработанный Джоном Бэкусом в International Business Machines (IBM) в 1954 году, был первым крупным языком программирования, который устранил препятствия, представленные машинным кодом при создании сложных программ. FORTRAN был компилируемым языком , который позволял использовать именованные переменные, сложные выражения, подпрограммы и многие другие функции, которые сейчас распространены в императивных языках. В последующие два десятилетия было разработано много других крупных императивных языков программирования высокого уровня. В конце 1950-х и 1960-х годах был разработан ALGOL , чтобы упростить выражение математических алгоритмов, и даже служил целевым языком операционной системы для некоторых компьютеров. MUMPS (1966) довел императивную парадигму до логической крайности, не имея вообще никаких операторов, полагаясь исключительно на команды, вплоть до того, что команды IF и ELSE стали независимыми друг от друга, связанными только внутренней переменной с именем $TEST. COBOL (1960) и BASIC (1964) были попытками сделать синтаксис программирования более похожим на английский. В 1970-х годах Pascal был разработан Никлаусом Виртом , а C был создан Деннисом Ритчи , когда он работал в Bell Laboratories . Вирт продолжил разрабатывать Modula-2 и Oberon . Для нужд Министерства обороны США Жан Ичбиа и команда из Honeywell начали проектировать Ada в 1978 году после 4-летнего проекта по определению требований к языку. Спецификация была впервые опубликована в 1983 году и пересмотрена в 1995, 2005 и 2012 годах.
В 1980-х годах наблюдался быстрый рост интереса к объектно-ориентированному программированию . Эти языки были императивными по стилю, но добавляли возможности для поддержки объектов . Последние два десятилетия 20-го века ознаменовались разработкой многих таких языков. Smalltalk -80, первоначально задуманный Аланом Кейем в 1969 году, был выпущен в 1980 году исследовательским центром Xerox в Пало-Альто ( PARC ). Опираясь на концепции другого объектно-ориентированного языка — Simula (который считается первым в мире объектно-ориентированным языком программирования , разработанным в 1960-х годах), — Бьярне Страуструп разработал C++ , объектно-ориентированный язык, основанный на C. Разработка C++ началась в 1979 году, а первая реализация была завершена в 1983 году. В конце 1980-х и 1990-х годах заметными императивными языками, опирающимися на объектно-ориентированные концепции, были Perl , выпущенный Ларри Уоллом в 1987 году; Python , выпущенный Гвидо ван Россумом в 1990 году; Visual Basic и Visual C++ (включая Microsoft Foundation Class Library (MFC) 2.0), выпущенные Microsoft в 1991 и 1993 годах соответственно; PHP , выпущенный Расмусом Лердорфом в 1994 году; Java , Джеймсом Гослингом ( Sun Microsystems ) в 1995 году, JavaScript , Бренданом Эйхом ( Netscape ), и Ruby , Юкихиро «Мац» Мацумото, оба выпущенные в 1995 году. .NET Framework (2002) от Microsoft является императивом по своей сути, как и его основные целевые языки, VB.NET и C#, которые работают на нем; однако F# от Microsoft , функциональный язык, также работает на нем.
FORTRAN (1958) был представлен как "Система IBM Mathematical FORmula TRANslating". Он был разработан для научных расчетов, без возможности обработки строк . Наряду с объявлениями , выражениями и операторами он поддерживал:
Это удалось, потому что:
Однако поставщики, не являющиеся IBM, также писали компиляторы Fortran, но с синтаксисом, который, скорее всего, не справился бы с компилятором IBM. [4] Американский национальный институт стандартов (ANSI) разработал первый стандарт Fortran в 1966 году. В 1978 году стандартом стал Fortran 77, действовавший до 1991 года. Fortran 90 поддерживает:
COBOL (1959) означает «COmmon Business Oriented Language». Fortran манипулировал символами. Вскоре стало ясно, что символы не обязательно должны быть числами, поэтому были введены строки. [5] Министерство обороны США оказало влияние на разработку COBOL, и Грейс Хоппер внесла основной вклад. Выражения были похожи на англоязычные и многословны. Целью было разработать язык, на котором менеджеры могли бы читать программы. Однако отсутствие структурированных утверждений помешало достижению этой цели. [6]
Разработка COBOL была строго под контролем, поэтому не возникло диалектов, требующих стандартов ANSI. Как следствие, он не менялся в течение 15 лет до 1974 года. Версия 1990-х годов действительно внесла существенные изменения, такие как объектно-ориентированное программирование . [6]
ALGOL (1960) означает «ALGOrithmic Language». Он оказал глубокое влияние на разработку языков программирования. [7] Возникнув из комитета европейских и американских экспертов по языкам программирования, он использовал стандартную математическую нотацию и имел читаемый структурированный дизайн. Algol был первым, кто определил свой синтаксис с помощью формы Бэкуса–Наура . [7] Это привело к появлению синтаксически-управляемых компиляторов. Он добавил такие функции, как:
Прямые потомки Algol включают Pascal , Modula-2 , Ada , Delphi и Oberon на одной ветви. На другой ветви есть C , C++ и Java . [7]
BASIC (1964) означает «Универсальный символьный код инструкций для начинающих». Он был разработан в Дартмутском колледже для всех студентов. [8] Если студент не переходил на более мощный язык, он все равно помнил Basic. [8] Интерпретатор Basic был установлен в микрокомпьютерах, произведенных в конце 1970-х годов. По мере роста индустрии микрокомпьютеров рос и язык. [8]
Basic был пионером интерактивного сеанса . [8] Он предлагал команды операционной системы в своей среде:
Однако синтаксис Basic был слишком прост для больших программ. [8] Недавние диалекты добавили структуру и объектно-ориентированные расширения. Visual Basic от Microsoft по-прежнему широко используется и создает графический пользовательский интерфейс . [9]
Язык программирования C (1973) получил свое название, потому что язык BCPL был заменен на B , а AT&T Bell Labs назвала следующую версию «C». Его целью было написание операционной системы UNIX . [10] C — относительно небольшой язык, что облегчает написание компиляторов. Его рост отражал рост аппаратного обеспечения в 1980-х годах. [10] Его рост также был обусловлен тем, что он имел возможности языка ассемблера , но использовал высокоуровневый синтаксис . Он добавил расширенные возможности, такие как:
C позволяет программисту контролировать, в какой области памяти будут храниться данные. Глобальные переменные и статические переменные требуют наименьшего количества тактов для хранения. Стек автоматически используется для стандартных объявлений переменных . Память кучи возвращается в переменную-указатель из malloc()
функции.
main()
. [12] Глобальные переменные видны main()
любой другой функции в исходном коде.main()
, других функций или внутри {
}
разделителей блоков являются локальными переменными . Локальные переменные также включают формальные переменные параметров . Переменные параметров заключаются в скобки определений функций. [13] Они предоставляют интерфейс к функции.static
префикса, также хранятся в глобальной и статической области данных. [11] В отличие от глобальных переменных, статические переменные видны только внутри функции или блока. Статические переменные всегда сохраняют свое значение. Примером использования может служить функцияint increment_counter(){ static int counter = 0; counter++; return counter;}
static
префикса, включая формальные переменные-параметры, [15] называются автоматическими переменными [12] и хранятся в стеке. [11] Они видны внутри функции или блока и теряют свою область действия при выходе из функции или блока.malloc()
библиотечную функцию для выделения памяти кучи. [17] Заполнение кучи данными является дополнительной функцией копирования. Переменные, хранящиеся в куче, экономично передаются функциям с помощью указателей. Без указателей весь блок данных должен был бы передаваться функции через стек.В 1970-х годах инженерам-программистам требовалась языковая поддержка для разбиения крупных проектов на модули . [18] Одной из очевидных функций было физическое разложение крупных проектов на отдельные файлы . Менее очевидной функцией было логическое разложение крупных проектов на абстрактные типы данных . [18] В то время языки поддерживали конкретные ( скалярные ) типы данных, такие как целые числа, числа с плавающей точкой и строки символов . Конкретные типы данных имели свое представление как часть своего имени. [ 19 ] Абстрактные типы данных — это структуры конкретных типов данных — с назначенным новым именем. Например, список целых чисел можно было бы назвать integer_list
.
В объектно-ориентированном жаргоне абстрактные типы данных называются классами . Однако класс — это всего лишь определение; память не выделяется. Когда память выделяется классу, он называется объектом . [ 20]
Объектно-ориентированные императивные языки, разработанные путем объединения потребности в классах и потребности в безопасном функциональном программировании . [21] Функция в объектно-ориентированном языке назначается классу. Назначенная функция затем называется методом , функцией -членом или операцией . Объектно-ориентированное программирование выполняет операции над объектами . [22]
Объектно-ориентированные языки поддерживают синтаксис для моделирования отношений подмножества/надмножества . В теории множеств элемент подмножества наследует все атрибуты, содержащиеся в надмножестве. Например, студент — это человек. Следовательно, множество студентов — это подмножество множества людей. В результате студенты наследуют все атрибуты , общие для всех людей. Кроме того, студенты имеют уникальные атрибуты, которых нет у других людей. Объектно-ориентированные языки моделируют отношения подмножества/надмножества с помощью наследования . [23] Объектно-ориентированное программирование стало доминирующей языковой парадигмой к концу 1990-х годов. [18]
C++ (1985) изначально назывался «C с классами». [24] Он был разработан для расширения возможностей C путем добавления объектно-ориентированных возможностей языка Simula . [25]
Объектно-ориентированный модуль состоит из двух файлов. Файл определений называется заголовочным файлом . Вот заголовочный файл C++ для класса GRADE в простом школьном приложении:
// сорт.h // -------// Используется для того, чтобы разрешить нескольким исходным файлам включать // этот заголовочный файл без ошибок дублирования. // См.: https://en.wikipedia.org/wiki/Imperative_programming/Include_guard // ---------------------------------------------- #ifndef GRADE_H #define GRADE_Hclass GRADE { public : // Это операция конструктора. // ---------------------------------- GRADE ( const char letter ); // Это переменная класса. // ------------------------- символ буква ; // Это операция-член. // --------------------------- int grade_numeric ( const char letter ); // Это переменная класса. // ------------------------- int numeric ; }; #endif
Операция конструктора — это функция с тем же именем, что и имя класса. [26] Она выполняется, когда вызывающая операция выполняет new
оператор.
Другой файл модуля — это исходный файл . Вот исходный файл C++ для класса GRADE в простом школьном приложении:
// grade.cpp // --------- #include "grade.h" GRADE :: GRADE ( const char letter ) { // Ссылаемся на объект, используя ключевое слово 'this'. // ---------------------------------------------- this -> letter = letter ; // Это временная связность // -------------------------- это -> числовое = grade_numeric ( letter ); } int GRADE :: grade_numeric ( const char letter ) { if ( ( letter == 'A' || letter == 'a' ) ) return 4 ; else if ( ( letter == 'B' || letter == 'b' ) ) return 3 ; else if ( ( letter == 'C' || letter == 'c' ) ) return 2 ; else if ( ( letter == 'D' || letter == 'd' ) ) return 1 ; else if ( ( letter == 'F' || letter == 'f' ) ) return 0 ; else return -1 ; }
Вот заголовочный файл C++ для класса PERSON в простом школьном приложении:
// person.h // -------- #ifndef PERSON_H #define PERSON_Hкласс ПЕРСОНА { public : ПЕРСОНА ( const char * name ); const char * name ; }; #endif
Вот исходный файл C++ для класса PERSON в простом школьном приложении:
// person.cpp // ---------- #include "person.h" ЧЕЛОВЕК :: ЧЕЛОВЕК ( const char * name ) { this -> name = name ; }
Вот заголовочный файл C++ для класса STUDENT в простом школьном приложении:
// student.h // --------- #ifndef STUDENT_H #define STUDENT_H#include "person.h" #include "grade.h" // STUDENT является подмножеством PERSON. // -------------------------------- class STUDENT : public PERSON { public : STUDENT ( const char * name ); ~ STUDENT (); GRADE * grade ; }; #endif
Вот исходный файл C++ для класса STUDENT в простом школьном приложении:
// student.cpp // ----------- #include "student.h" #include "person.h" STUDENT :: STUDENT ( const char * name ) : // Выполнить конструктор суперкласса PERSON. // ------------------------------------------------- PERSON ( name ) { // Больше ничего не нужно делать. // ------------------- } STUDENT ::~ STUDENT () { // освободить память оценки // чтобы избежать утечек памяти. // ------------------------------------------------- удалить это -> оценка ; }
Вот программа-драйвер для демонстрации:
// student_dvr.cpp // --------------- #include <iostream> #include "student.h" int main ( void ) { СТУДЕНТ * студент = new СТУДЕНТ ( "Студент" ); студент -> оценка = new GRADE ( 'a' ); std :: cout // Обратите внимание, что student наследует имя PERSON << student -> name << ": Числовая оценка = " << student -> grade -> numeric << " \n " ; // освободить память ученика // чтобы избежать утечек памяти. // ------------------------------------------------- удалить ученика ; вернуть 0 ; }
Вот makefile для компиляции всего:
# makefile # -------- все : student_dvr очистить : rm student_dvr *.ostudent_dvr : student_dvr . cpp оценка . o student . o персона . o c++ student_dvr. cpp оценка.o student.o персона.o -o student_dvr класс.o : класс . cpp класс . h c++ -c класс.cpp студент.o : студент .cpp студент .h c ++ -c студент.cpp person.o : person . cpp person . h c++ -c person.cpp