
Парадигмы программирования.
Парадигма программирования — это совокупность идей и понятий, определяющая стиль написания программ.
Императи́вная парадигма описывает процесс вычисления в виде инструкций, изменяющих состояние программы. Императивная программа очень похожа на приказы, выражаемые повелительным наклонением в естественных языках, то есть это последовательность команд, которые должен выполнить компьютер. Основана на модели конечного автомата Тьюринга-Поста.
Первыми императивными языками были машинные коды — родной язык программирования для компьютера. В этих языках инструкции были крайне просты, что снижало нагрузку на компьютеры, однако затрудняло написание крупных программ. В 1954 появился первый «человеческий» язык программирования — FORTRAN, затем ALGOL, COBOL, BASIC, Pascal, C.
Одна из характерных черт императивного программирования - наличие переменных с операцией "разрушающего присвоения". То есть, была переменная А, было у нее значение Х. Алгоритм предписывает на очередном шаге присвоить переменной А значение Y. То значение, которое было у А, будет "навсегда забыто".
Императивное программирование наиболее пригодно для реализации небольших подзадач, где очень важна скорость исполнения на современных компьютерах. Кроме этого, работа с внешними устройствами, как правило, описывается в терминах последовательного исполнения операций ("открыть кран, набрать воды"), что делает такие задачи идеальными кандидатами на императивную реализацию.
Выбор рамок императивной парадигмы для обучения основам программирования, по-видимому, ни у кого не вызывает сомнения. Этому есть несколько причин:
императивная парадигма наиболее близка природе человека и интуитивному понятию алгоритма на ранних стадиях развития мышления (есть положительный опыт развивающего обучения с элементами алгоритмизации уже в начальной школе);
программирование в рамках императивной парадигмы эффективно для широкого класса задач, многие из которых попадают в зону ближайшего развития учащихся в старших классах базовой школы;
императивная парадигма наиболее близка природе компьютера, основным принципам его функционирования, так как, не смотря на всю сложность современного компьютера, на уровне аппаратной части его можно по-прежнему рассматривать как некоторый автомат (процессор+память+…) с конечным множеством состояний (содержимого памяти);
доля программных продуктов, созданных исключительно в рамках декларативной парадигмы программирования мала; как правило, при решении задач используется сочетание парадигм, одной из которых является императивная;
большой выбор систем программирования в виде самостоятельных программных средств и в виде интегрированных в другие системы подсистем, позволяющих разрабатывать программные продукты с использованием императивной парадигмы;
обширная номенклатура учебных, справочных и прочих публикаций по соответствующим системам программирования в бумажном и электронном видах на различных носителях и в глобальной сети.
Недостаток: в чистом виде позволяет решать только очень простые задачи.
Событийно-управляемое программирование - программирование, при котором задаются реакции программы на различные события (действия пользователя). СУП можно рассматривать как «потомок» императивной парадигмы. СУП имеет 2 подкласса:
1.Параллельное программирование представляет программу в виде набора сообщающихся процессов, которые могут выполняться параллельно. Такие программы могут выполняться как на одном процессоре (чередуя выполнение шагов каждого процесса), так и на нескольких.
В системе параллельных процессов каждый отдельный процесс обрабатывает события. События могут быть как общими для всей системы, так и индивидуальными для одного или нескольких процессов. В таких терминах достаточно удобно описывать, например, элементы графического интерфейса пользователя, или моделирование каких-либо реальных процессов (например, управление уличным движением) - так как понятие события является для таких задач естественным.
2.Объектно-ориентированное программирование - технология программирования, при которой программа рассматривается как набор объектов и их взаимодействий. Каждый объект программы является экземпляром некоторого класса; — классы могут наследовать атрибуты и методы их родительских классов, в то же время добавляя свои собственные. Иерархия классов позволяет моделировать сущности решаемой задачи на нескольких уровнях детализации и в дальнейшем использовать класс, отвечающий уровню детализации, необходимому для решения конкретной подзадачи.
Важно выделить следующие основные свойства объектов:
1.) Так как один объект может воздействовать на другой исключительно при помощи посылки последнему сообщений, он не может как-либо непосредственно работать с собственными данными "собеседника", и, следовательно, не может нарушить их внутреннюю согласованность. Это свойство (сокрытие данных) принято называть инкапсуляцией.
2.) Так как объекты взаимодействуют исключительно за счет обмена сообщениями, объекты-собеседники могут ничего не знать о реализации обработчиков сообщений у своего визави. Взаимодействие происходит исключительно в терминах сообщений/событий, которые достаточно легко привязать к предметной области. Это свойство (описание взаимодействия исключительно в терминах предметной области) называют абстракцией.
3.) Объекты взаимодействуют исключительно через посылку сообщений друг другу. Поэтому если в каком-либо сценарии взаимодействия объектов заменить произвольный объект другим, способным обрабатывать те же сообщения, сценарий так же будет реализуем. Это свойство (возможность подмены объекта другим объектом со сходной структурой класса) называется полиморфизмом.
Многие современные языки поддерживают ООП, хотя и в разной степени: — чисто объектно-ориентированные языки, например, Smalltalk и Ruby, разработаны для того, чтобы поддерживать и даже навязывать объектно-ориентированный стиль разработки, и не поддерживают другие стили программирования; — преимущественно объектно-ориентированные языки, например, Java, C++ и Python, разработаны в основном для поддержки ООП, но позволяют использовать элементы процедурного программирования; — исторически процедурные языки, например, Perl и Fortran 2002, были доработаны и в них была добавлена поддержка некоторых элементов ООП.
Декларативная парадигма программирования определяет процесс вычислений посредством описания логики самого вычисления, а не управляющей логики программы.
Декларативное программирование является противоположностью императивного программирования; первое описывает, что необходимо сделать, а второе — как именно это сделать.
Наиболее важными разновидностями декларативного программирования, являются функциональное и логическое (или реляционное) программирование.
1.Функциональное программирование представляет собой одну из альтернатив императивному подходу. Оно основано на лямбда-исчислении Черча. В императивном программировании алгоритмы - это описания последовательно исполняемых операций. Здесь существует понятие "текущего шага исполнения" (то есть, времени), и "текущего состояния", которое меняется с течением этого времени.
В функциональном программировании понятие времени отсутствует. Программы являются выражениями, исполнение программ заключается в вычислении этих выражений.
Так как порядок вычисления подвыражений не имеет значения, функциональное программирование может быть естественным образом реализовано на платформах, поддерживающих параллелизм.
Функциональное программирование, как и другие модели "неимперативного" программирования, обычно применяется для решения задач, которые трудно сформулировать в терминах последовательных операций. Практически все задачи, связанные с искусственным интеллектом, попадают в эту категорию. Среди них следует отметить задачи распознавания образов, общение с пользователем на естественном языке, реализацию экспертных систем, автоматизированное доказательство теорем, символьные вычисления. Эти задачи далеки от традиционного прикладного программирования, поэтому им уделяется не так много внимания в учебных программах по информатике.
Логическое программирование
В функциональном программировании программы - это выражения, и их исполнение заключается в вычислении их значения. В логическом программировании программа представляет из себя некоторую теорию (описанную на достаточно ограниченном языке), и утверждение, которое нужно доказать. В доказательстве этого утверждения и будет заключаться исполнение программы.
Логическое программирование и язык Пролог появились в результате исследования в области анализа естественных языков. Впоследствии было обнаружено, что логическое программирование столь же эффективно в реализации других задач искусственного интеллекта.
Логическое программирование допускает естественную параллельную реализацию.
Язык Си.
Язык С и его последующие реализации C++ и C# занимают особое место среди языков программирования. Наряду со средствами языков высокого уровня, реализующими концепцию нисходящего структурного проектирования, язык содержит средства для программирования на низком, системном уровне (адрес значения, значение по указанному адресу, побитовые операции, операции сдвига). Благодаря гибкости и компактности своих конструкций С завоевал наибольшую популярность в среде профессиональных программистов и широко используется при разработке системных и прикладных программ.
Средства языков семейства С, декларируемые как преимущества, при некорректном использовании становятся недостатками. Гибкость языка достигается в основном за счет снижения контроля над правильностью использования данных различных типов и предоставления программисту возможности непосредственного изменения содержимого ячеек памяти. Отказ от проверки соответствия типов данных предоставляет программисту наибольшую свободу. Однако, ответственность за корректность программ при этом полностью ложится на программиста. Средства непосредственной работы с содержимым ячеек памяти позволяют эффективно использовать возможности конкретного компьютера. Однако, такие мощные средства требуют от программиста осторожности, аккуратности и хорошего знания языка и особенностей распределения памяти конкретного компьютера.
Программа на языке С может иметь следующую структуру:
программа С <раздел препроцессора>
<раздел функций>
<раздел препроцессора> #<опция препроцессора>
{#<опция препроцессора>}
В системах программирования семейства С используется двухпроходный транслятор типа компилятор. На первом проходе обрабатываются директивы препроцессора, на втором формируется вариант программы в машинных кодах.
Начинается программа с подключения различных «модулей», необходимых для работы определенных функций (ввод из файл\вывод в файл, работа со строками, графикой и т.д.)
Для этого пишется:
#include <имя модуля.расширение>
Так, например, в результате обработки директивы #include <<имя файла>> (внешние угловые скобки – символы языка С) в текст программы вместо этой директивы будет помещен файл описания стандартных функций системы (файл библиотеки). Файлы библиотеки языка С имеют расширение h (например, stdio.h – файл описания функций ввода/вывода).
Директива #define <идентификатор> <конструкция С> задает так называемое макроопределение. В результате обработки этой директивы каждое вхождение <идентификатор> в текст программы после макроопределения заменяется на <конструкция С>. Например, #define n 100 – макроопределение целочисленной константы n.
Внимание: в языке С строчные и прописные буквы во всех конструкциях различаются. Так N и n – различные идентификаторы.
Далее идут глобальные переменные, константы, функции и прочее, прочее.
Любая программа на языке С состоит из одной и более функций, одна из которых должна иметь имя main, и именно ей передается управление из операционной системы. Функция - это коллективное имя для некоторой группы описаний и операторов, заключенных в фигурные скобки { } и являющихся телом функции.
раздел функций> <описание функции>
{<описание функции>}
<описание функции> [<идентификатор типа>] <идентификатор>([< формальные параметры>])
<составной оператор>
Здесь <идентификатор типа> – тип возвращаемого значения (тип значения <идентификатор>).
<формальные параметры>
<идентификатор типа> <идентификатор>{, <идентификатор типа> <идентификатор>}
Идентификатор типа |
Размер, бит |
Диапазон |
unsigned char |
8 |
0÷255 |
Char |
8 |
-128÷127 |
Enum |
16 |
-32768÷32767 |
unsigned short |
16 |
0÷65535 |
Short |
16 |
-32768÷32767 |
unsigned int |
16 |
0÷65535 |
Int |
16 |
-32768÷32767 |
unsigned long |
32 |
0÷4294967295 |
Long |
32 |
-2147483648÷2147483647 |
Float |
32 |
3.4∙10-38÷3.4 10+38 |
Double |
64 |
1.7∙10-308÷1.7 10+308 |
long double |
80 |
1.7 10-4932÷1.7 10+4932 |
<идентификатор типа> задается стандартными служебными словами int (целый), long (длинный), short (короткий), unsigned (беззнаковый), char (символьный), float (действительный одинарной точности), double (действительный двойной точности), pointer (указатель), enum (перечислимый). Допустимые в языке типы данных, размеры выделяемой памяти в битах и диапазон представления приведены в табл. 1.1.
<составной оператор> {
<оператор>|<выражение>;
{<оператор>|<выражение>;}
}
Здесь первая открывающаяся { и последняя закрывающаяся } являются символами алфавита языка С. Значение функции определяется значением <выражения> необязательного оператора return <выражение>, вложенного в <составной оператор>. Если значение функции не определяется (оператор return <выражение> отсутствует), то рекомендуем в качестве типа значения использовать void (отсутствие типа)
Синтаксис оператора return: return [выражение]
Оператор return прекращает выполнение функции, в которой он появляется и передает управление на вызов функции. Выполнение программы продолжается непосредственно с той точки, откуда был произведен вызов функции. Значение выражения, если оно есть, передается на вызов функции. Если выражение не задано, то возвращаемое функцией значение не определено.
По общему соглашению аргумент "выражение" оператора return заключается в скобки. Однако, для языка С присутствие этих скобок необязательно.
Если в определении функции отсутствует оператор return, то управление автоматически передается на вызов функции после выполнения последнего оператора вызванной функции. При этом не определено возвращаемое вызванной функцией значение. Если от функции не требуется возврат какого-либо значения, то функция объявляется с возвращаемым значением типа void.
В <составном операторе> могут содержаться операторы резервирования памяти, с помощью которых описываются локальные переменные
<резервирование памяти> <идентификатор типа> <идентификатор>{, <идентификатор>}
Например, оператор int i, j; описывает переменные i и j как целые, а оператор float t; определяет переменную t как действительную одинарной точности.