Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Обзор языков программирования высокого уровня

Содержание:

Введение

Появление речи стало важным этапом в развитии человечества. Именно речь дала возможность людям общаться на более высоком уровне, эффективно взаимодействовать. Постепенно из примитивных жестов и звуков сформировались современные естественные языки. Спустя тысячелетия перед людьми стала новая задача – научиться «общаться» с техникой. Это потребовало создания новых языков, искусственных – языков программирования, которые могли бы быть «понятны» и людям, и компьютерам.

Если изначально языки программирования были низкоуровневыми, ориентированными на то, чтобы быть понятными для конкретного компьютера, то по мере развития техники стали появляться языки высокого уровня, более универсальные и удобные для человека. Задачи «перевода» на машинный язык стали перекладываться на специализированные программы. В настоящее время разработаны тысячи языков программирования высокого уровня, каждый из которых имеет свои особенности, удобен для решения тех или иных задач. Как нельзя сказать, что русский язык лучше китайского, так нельзя выбрать один лучший язык программирования – для написания web-приложений используются одни языки, а для программирования станков – совершенно другие. Выучить все языки невозможно, но нужно четко понимать, какие языки бывают и чем они отличаются. Таким образом, актуальность темы работы не вызывает сомнений.

Целью работы является обзор языков программирования высокого уровня.

Для достижения цели необходимо решить ряд задач, в соответствии с которыми построена структура работы:

1) рассмотреть теоретические основы языков программирования высокого уровня – проследить историю развития, раскрыть понятие языков программирования высокого уровня в противопоставлении языкам программирования низкого уровня;

2) провести классификацию языков программирования высокого уровня по способу трансляции и базовой парадигме программирования (императивные и декларативные языки);

3) охарактеризовать самые популярные языки программирования высокого уровня – Java, C/C++.

1 Теоретические основы языков программирования высокого уровня

1.1 История появления языков программирования высокого уровня

История программирования насчитывает почти два века. Еще в 20-х годах XIX в. Ч. Бэббидж высказал мысль о пред­варительной записи порядка действий машины для последующей автоматической реализации вычислений, что привело к созданию автоматических цифровых вычислительных машин. С этого момента начинается история программирования.

Первым в мире программистом считают Аду Лавлейс. Она тео­ретически разработала некоторые приемы управления последова­тельностью вычислений и описала одну из важнейших и сегодня алгоритмических конструкций — цикл.

Революционным этапом в истории программирования стало созда­ние электронных вычислительных машин (ЭВМ). Однако процессор компьютера понимает только язык машинных команд в двоичных кодах. Программист, пишущий в машинных кодах, должен помнить вес числовые коды машинных команд и самостоятельно распределять память под хранение данных и программы.

Большой вклад в развитие языков программирования висела Грейс Мюррсй Хоппер. Группой ученых пол се руководством были приду­маны подпрограммы, создан первый в мире компилятор (ими же был введен и сам этот термин), введено понятие «отладка» (debugging).

Появление языков типа «Автокод-Ассемблер» привело к тому, что переменные величины стали изображаться символьными име­нами, а числовые коды операций были заменены на мнемонические коды. Язык программирования стал более понятен для человека, но удалился от языка машинных команд, понятного процессору. Чтобы компьютер мог исполнить программу, написанную на Ас­семблере, необходимо было вначале перевести ее на язык машинных команд с помощью транслятора. Языки типа Ассемблера являются машинно-ориентированными языками, т. е. настроены на структуру машинных команд конкретного процессора. Поэтому компьютеры с различными тинами процессоров имеют разный Ассемблер.

В середине 1950-х гг. вычислительные машины получили широкое распространение в университетах и научно-исследовательских институтах США и Западной Европы: тогда же наступило и время стремительного прогресса в области программирования. Новые разработки не отрицали всего, что было сделано прежде: компиляторы и интерпретаторы для языков Ассемблера остались важным средством программирования для любого компьютера. Но программисты решили еще более облегчить себе работу. Посредниками между программистом и компьютером стали языки программирования высокого уровня. От Ассемблера они отличались большей гибкостью и возможностью использования конструкций, подобных предложениям на естественном языке.

Конечно, как только появились компьютеры, нужно было со­здавать для них программы, и проблема написания управляющего кода, понятного машине, стояла задолго до появления ПЭВМ, - например, один из первых языков программирования высокого уровня (FORTRAN) появился еще в 1957 г. Тем не менее с появлением нового типа компьютеров пришлось пересмотреть некоторые старые концеп­ции разработки языков, ведь все старые языки были ориентированы на суперкомпьютеры, архитектура которых существенно отличалась от архитектуры персональных компьютеров, да и задачи перед ними стояли разные. Как известно, «большие машины» чаще всего исполь­зовались в военных целях или для научных исследований.

Если не учитывать работы Конрада Цузе, который придумал алго­ритмический язык для своего механического компьютера, и прямое программирование в машинных кодах, то можно сказать, что началом эволюции языков программирования стали различные ассемблеры, которые позволили программисту отвлечься от запоминания шестнадцатеричных кодов команд и регистров. С присвоением им сим­вольных имен программирование стало более продуктивным и более привлекательным. Но даже на Ассемблере программировать было не очень удобно. Ассемблеры были аппаратно зависимыми, т. е. если, например, планировалось использовать программы на компьютере фирмы DEC, а не IBM, то приходилось переписывать весь про­граммный код заново. С появлением же языков высокого уровня программисты получили возможность разрабатывать алгоритм решения задачи, практически не тратя усилий на его программную реализа­цию.

Таким образом, появление языков высокого уровня ознаменовало первый шаг на пути создания прикладных программ, выходящих за пределы научно-исследовательских лабораторий. Но вместе с тем языки программирования становились все более и более сложными. Если в Ассемблере основная проблема заключалась в постоянном запоминании ненужных, по сути, сведений по ходу написания про­граммы и в утомительном кодировании мельчайших подробностей работы программы, то в современном языке C++ содержится такое ко­личество лексем, что далеко не каждый может освоить этот язык. Это, в свою очередь, привело к необходимости появления «упрощенных» систем и сред программирования, таких как, например. Visual Basic.

1.2 Языки программирования низкого и высокого уровня

Прогресс в сфере вычислительной техники и информационных и коммуникационных технологий предопределил появление разнооб­разных знаковых систем для записи алгоритмов – языков програм­мирования.

Язык программирования служит двум взаимосвязанным целям: предоставляет программисту аппарат для задания действий, которые должны быть выполнены компьютером, и формирует концепции, которыми пользуется программист, продумывая будущий алгоритм. Первой цели идеально отвечает язык, который настолько «близок к ЭВМ», что всеми основными машинными объектами можно легко и просто оперировать достаточно очевидным для программиста образом. Второй же цели идеально отвечает язык, который настолько близок к решаемой задаче, что концепции ее решения можно выра­жать прямо и коротко.

Вначале рассмотрим классификацию языков программирования по близости языка к аппаратному обеспечению (рис. 1).

Языки програм-мирования

Языки низкого уровня

Языки высокого уровня

Машинные

Машинно-ориентиро-ванные

Процедурно-ориентиро-ванные

Проблемно-ориентиро-ванные

Рисунок 1 – Классификация языков программирования по близости языка к аппаратному обеспечению

Первые языки программирования (как и первые ЭВМ) были довольно примитивны и ориентированы на различные вычисления - как на чисто теоретические научные расчеты (прежде всего математические и физические), так и на прикладные задачи (в частности, в военной области). Программы, написанные на них, представляли собой линейные последовательности элементарных операций с регистрами, в которых хранились данные.

Следует отметить, что ранние языки программирования были оптимизированы под аппаратную архитектуру конкретного компью­тера, для которого они предназначались. Это обеспечивало высокую эффективность вычислений, но не позволяло обеспечить стандартиза­цию: программа, работоспособная на одной вычислительной машине, не могла выполняться на другой.

Машинно-ориентированные языки программирования – это языки, наборы операторов и изобразительные средства которых существенно зависят от особенностей ЭВМ (ее системы команд, структуры памяти и т. д.).

Машинно-ориентированные языки обеспечивают:

  • высокое качество создаваемых программ (компактность и ско­рость выполнения);
  • возможность максимально полного использования конкретных аппаратных ресурсов;
  • предсказуемость объектного кода и запросов на выделение памяти.

Недостатки машинно-ориентированных языков:

  • для составления эффективных программ необходимо знать си­стему команд и особенности функционирования конкретной ЭВМ:
  • процесс составления программ очень трудоемок, плохо защищен от появления ошибок;
  • низкая скорость программирования;
  • невозможность непосредственного использования программы на ЭВМ других типов [5, c. 10].

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

Последующее десятилетие ознаменовалось появлением так назы­ваемых «языков программирования высокого уровня» (по сравнению с их предшественниками, получившими, соответственно, название «языки низкого уровня»).

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

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

Важно, что с появлением языков высокого уровня зависимость программ от аппаратного обеспечения ЭВМ существенно уменьши­лась. Платой за это стала необходимость создания трансляторов - специализированных программ, преобразующих инструкции языков высокого уровня в коды той или иной ЭВМ, а также некоторая потеря в скорости вычислении, которая, впрочем, компенсировалась существенным выигрышем в скорости разработки программ и уни­фикацией программного кода.

Транслятор - это специальная программа, переводящая текст программы на языке программирования в текст эквивалентной программы на языке машинных команд.

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

Конечно, для обучения новым языкам программирования требо­вались дополнительные затраты времени и средств, а эффективность реализации программ на прежнем аппаратном обеспечении несколько снижалась. Однако это были временные трудности, и, как показала практика, многие из даже самых первых языков высокого уровня оказались настолько удачными, что активно используются и сегодня. Одним из примеров является FORTRAN, реализующий вычислитель­ные алгоритмы. Другой пример - язык APL, позже трансформировав­шийся в BPL, а затем - в С («Си»). Основные его конструкции оста­ются неизменными вот уже несколько десятилетий и присутствуют в современных версиях - C++ и С#. Точно так же и сегодня хорошо известны языки ALGOL, COBOL, Pascal. BASIC и др.

Итак, чем выше уровень языка, тем меньше трудоемкость программиро­вания на нем. Машинные же языки современных ЭВМ практически не используются для непосредственного программирования.

2 Классификация языков программирования высокого уровня

2.1 Виды языков программирования высокого уровня по способу трансляции

Со времени создания первых программируемых машин человечество придумало около десяти тысяч языков программирования. Некоторыми языками умеет пользоваться только небольшое число их собственных разработчиков, другие известны миллионам людей.

Языки программирования могут классифицироваться по различным основаниям, общепринятой системы в этом вопросе не выработано.

Выше была рассмотрена классификация языков программирования по уровню взаимодействия с аппаратным обеспечением (языки программирования высокого и низкого уровня).

Можно выделить два принципиально разных способа реализации языков программирования: компиляция и интерпретация. Распространено заблуждение, согласно которому способ реализации является присущим конкретному языку свойством. В действительности, это деление до определенной степени условно. В ряде случаев язык имеет формальную семантику, ориентированную на интерпретацию, но все или почти все его действительные реализации являются компиляторами, порой весьма эффективно оптимизирующими (примерами могут служить языки семейства ML, такие как Standard ML, Haskell). Кроме того, большинство современных «чистых» интерпретаторов не исполняют конструкции языка непосредственно, а компилируют их в некоторое высокоуровневое промежуточное представление (например, с разыменованием переменных и раскрытием макросов). Для любого интерпретируемого языка можно создать компилятор - например, язык Лисп, изначально интерпретируемый, может компилироваться без каких бы то ни было ограничений. [1, c. 208].

Рассмотрим различия компиляции и интерпретации как двух разновидностей трансляции. Программу, являющуюся входными данными транслятора, будем называть исходной программой (source program). Компилятор (compiler) переводит исходную программу в эквивалентную программу на языке, понятном компьютеру, то есть на машинном языке (рис. 2).

Рисунок 2 – Процесс компиляции и последующего выполнения программы

Процесс компиляции состоит из двух частей: анализа и синтеза. Анализирующая часть компилятора разбивает исходную программу на составляющие ее элементы (конструкции языка) и создает промежуточное представление исходной программы. Синтезирующая часть из промежуточного представления создает новую, целевую, программу. Отметим, что однажды полученная в результате компиляции целевая программа может в дальнейшем выполняться много раз с различными входными данными. Далеко не всегда исходные программы корректны с точки зрения исходного языка. Более того, некорректные программы подаются на вход компилятору значительно чаще, чем корректные – таков современный процесс разработки программ. Поэтому крайне важной частью процесса компиляции является точная диагностика ошибок, допущенных во входной программе.

В отличие от компилятора интерпретатор не создает никакой новой программы. Входными данными интерпретатора является не только исходная программа, но и входные данные самой исходной программы (рис. 3).

Рисунок 3 – Процесс интерпретации

Интерпретатор, так же, как и компилятор, анализирует программу на входном языке, создает промежуточное представление, а затем выполняет операции, содержащиеся в тексте этой программы. Например, интерпретатор может построить дерево разбора, а затем выполнить операции, которыми помечены узлы этого дерева. В том случае, если исходный язык достаточно прост (например, если это язык ассемблера или Basic), то никакое промежуточное представление не нужно, и тогда интерпретатор – это простой цикл. Он выбирает очередную инструкцию языка из входного потока, анализирует и выполняет ее. Затем выбирается следующая инструкция. Этот процесс продолжается до тех пор, пока не будут выполнены все инструкции, либо пока не встретится инструкция, означающая окончание процесса интерпретации.

Понятно, что при повторном запуске программы она должна интерпретироваться с самого начала. Интерпретация приводит к более гибкой и лучшей диагностике ошибок, чем компиляция. Поскольку исходная программа исполняется непосредственно, интерпретатор может включать хороший отладчик (debugger). Кроме того, интерпретатор может легко справиться с языками, позволяющими создавать программы, некоторые характеристики которых (например, размеры и типы переменных) могут зависеть от входных данных [13, c. 17].

Как правило, скомпилированные программы выполняются быстрее и не требуют для выполнения дополнительных программ, так как уже переведены на машинный язык. Вместе с тем, при каждом изменении текста программы требуется её перекомпиляция, что замедляет процесс разработки. Кроме того, скомпилированная программа может выполняться только на том же типе компьютеров и, как правило, под той же операционной системой, на которую был рассчитан компилятор. Чтобы создать исполняемый файл для машины другого типа, требуется новая компиляция. Интерпретируемые языки позволяют запускать программы сразу же после изменения, причём на разных типах машин и операционных систем без дополнительных усилий, а гомоиконные – и вовсе динамически перемещать программу между разными машинами без прерывания её работы.

Итак, компилятор переводит программу на машинный язык сразу и целиком, создавая при этом отдельную программу, а интерпретатор переводит на машинный язык прямо во время исполнения программы. Примеры компилируемых языков - C, C++, Pascal, примеры интерпретируемых - PHP, JavaScript, Python. Распространен и смешанный подход. Например, Java и С# относятся именно к компилируемо-интерпретируемым языкам программирования. А именно, программа компилируется не в машинный язык, а в машинно-независимый код низкого уровня, байт-код. Далее байт-код выполняется виртуальной машиной. Для выполнения байт-кода обычно используется интерпретация, хотя отдельные его части для ускорения работы программы могут быть транслированы в машинный код непосредственно во время выполнения программы по технологии компиляции «на лету» (Just-in-time compilation, JIT).

2.2 Императивные языки программирования высокого уровня

Под парадигмой программирования понимается набор представлений о некотором классе программных систем, допускающих реализацию с помощью этой парадигмы «способа мыслить о компьютерной системе» [2, с. 176]. Таким образом, парадигма не есть язык или система программирования, а некоторая логика, в рамках которой программы могут писаться на разных языках. Языки программирования ориентированы на те или иные парадигмы, и, соответственно, представляют специальные средства для удобства ее реализации.

Можно выделить две важнейших парадигмы – императивную и декларативную.

Программа, написанная на императивном языке, явно указывает способ получения желаемого результата, не определяя при этом его ожидаемых свойств. Процедура получения желаемого результата имеет вид последовательности операций, поэтому для императивных языков характерно указание порядка выполнения операторов. В основе такого программирования лежат извлечение из памяти значения некоторой переменной, совершение над ним действия и сохранение нового значения с помощью оператора присваивания. Императивные языки тесно связаны с фон-неймановской моделью вычислений [8, с. 82].

Структурное программирование — парадигма программирования (также часто встречающееся определение — методология разработки), которая была первым большим шагом в развитии императивного программирования. Основоположниками структурного программирования были Э. Дейкстра и Н. Вирт. Языками-первопроходцами в этой парадигме были Fortran, Algol и B, позже их приемниками стали Pascal и C. Эта парадигма ввела новые понятия, объединяющие часто используемые шаблоны написания императивного кода. В структурном программировании программист по-прежнему оперирует состоянием и инструкциями, однако вводится понятие составной инструкции (блока), инструкций ветвления и цикла [7].

Следующим шагом стал процедурный подход. При этом подходе какой-то код программы мог объединяться в отдельные блоки (подпрограммы – процедуры и функции). После этого такой блок команд можно вызывать из любой части программы. Любая процедура может быть вызвана из любой точки программы, включая другие процедуры или ее же саму (рекурсивный вызов). Передача данных из главной программы в подпрограмму и возврат результата выполнения функции осуществляются с помощью параметров. Параметром называется переменная, которой присваивается некоторое значение в рамках указанного применения. Различают формальные параметры – параметры, определенные в заголовке подпрограммы, и фактические параметры – выражения, задающие конкретные значения при обращении к подпрограмме. При обращении к подпрограмме ее формальные параметры замещаются фактическими, переданными из главной программы [11, c. 197].

Процедурное программирование широко используется в крупномасштабных проектах, когда на первый план выходят следующие преимущества:

  • легкость повторного использования фрагментов кода, оформленных как процедуры (например, в библиотеках);
  • легкость прослеживания логики программы;
  • возможность сопровождения программного продукта через длительное время после написания кода или кем-то, кроме его автора.

Следует отметить, что принципиально разными типами подпрограмм являются процедуры и функции. Их отличие состоит в том, что функции возвращают некоторое значение, ассоциированное с их именем; этот результат может использоваться в выражениях. В качестве математического примера можно привести выражение y:=sin(x)+cos(x); здесь синус и косинус – это функции. Процедуры такого значение не имеют; все значения возвращаются только через изменение значений параметров. В некоторых языках существуют отдельные инструменты для реализации функций и процедур (например, в Pascal – это procedure и function), в других такого разделения не производится (например, в С++ используются только функции, но можно указать, что она возвращает «пустое» - void – значение).

Развитием процедурного программирования стало модульное программирование, позволяющее реализовать более масштабные проекты. В соответствии с этой парадигмой подпрограммы группируются в отдельные файлы – модули – которые уже образуют проект в целом. Это упрощает отладку программы в целом и более удобно при работе команды разработчиков.

Еще одной парадигмой в рамках императивного программирования, приобретшей популярность в последние годы, стало объектно-ориентированное программирование. Основная идея объектно-ориентированной парадигмы заключается в построении модели на основе выделения понятий предметной области с последующим распределением обязанностей между ними. Программа, решающая пашу задачу, строится так, как задача решалась бы в жизни. По сути, строится модель предметной области, понятия предметной об­ласти представляются в виде классов, а их конкретные проявления в виде объ­ектов. Распределяя обязанности между классами, проектировщик приложения «оживляет» объекты, наделяет их поведением. Оживление (Animation) являет­ся основным принципом, используемым при построении архитектуры объектно-ориентированного приложения [10, c. 181].

Моделирование осуществляется декомпозицией не по действиям (глаголам), как было принято в императивном программировании до появления объектно-ориентированной парадигмы, а по сущностям (суще­ствительным). Сущности представляют собой, как правило, понятия предмет­ной области, но иногда искусственные понятия, которых нет в явном виде в предметной области, но без которых невозможно создать масштабируемый код.

При оживлении пользователю объектов совсем не обязательно знать внут­реннюю структуру объекта, так же как в реальной жизни пользователь обыч­но не знает, как в точности устроена микроволновая печь или телевизор, что не мешает ему разогревать еду и просматривать телепередачи. Скрытие внут­ренней реализации объекта от пользователя — это обычная практика, которая называется инкапсуляцией. Важна только функциональность объекта, его ин­терфейс. Такой подход позволяет ограничить распространение изменений в ко­де класса на все приложение. Это один из важнейших принципов объектно-ориентированного проектирования.

Удобный подход к организации программ «отдельная работа отдельно программируется и отдельно выполняется» успешно показал себя при развитии операционной системы UNIX как работоспособный принцип декомпозиции программ. Но существуют задачи, например, реализация систем программирования, в которых прямое следование такому принципу может противоречить требованиям к производительности. Возможен компромисс «отдельная работа программируется отдельно, а выполняется взаимосвязано с другими работами», что требует совмещения декомпозиции программ с методами сборки – комплексации или интеграции программ из компонентов. Рассматривая комплексацию как еще одну «отдельную» работу, описываемую, например, в терминах управления процессами, можно констатировать, что эта работа больше определяет требования к уровню квалификации программиста, чем объем программирования. При достаточно объективной типизации данных и процессов, возникающих при декомпозиции и сборке программ определенного класса, строят библиотеки типовых компонентов и разрабатывают компонентные технологии разработки программных продуктов – Corba, COM/DCOM, UML и т. п. Одна из проблем применения таких компонентов – их обширность.

Таким образом, ООП может отражать эволюцию подходов к организации структур данных на уровне задач и программ их решения, исходя из парадигмы императивно-процедурного программирования. От попыток реализации математически корректных абстрактных типов данных произошел практичный переход к технически простому статическому контролю типов данных при разработке и применении расширяемых программ. Расширение программы выполняется декларативно, а выбор нужного варианта при исполнении функций, обладающих неединственным определением, – в зависимости от типа данных. Введены дополнительные механизмы: инкапсуляция, уточнение типов данных при компиляции и выбор обработчиков данных, управляемый типами данных.

2.3 Декларативные языки программирования высокого уровня

Альтернативой для императивной парадигмы является декларативная парадигма. Декларативные языки – это класс языков программирования, программа на которых задаёт связи и отношения между объектами и величинами и не определяет последовательность выполнения действий. В программе в явном виде указывается, какими свойствами должен обладать результат, но не говорится, каким способом он будет получен. В силу этого такие понятия, как упорядочение и поток команд управления, не имеют никакого отношения к этим языкам, и в них отсутствуют операторы присваивания. Декларативные языки не привязаны к классической фон-неймановской модели вычислений, и в типичном случае алгоритм достижения требуемого результата может иметь высокую степень параллелизма.

В рамках декларативной парадигмы можно выделить функциональное и логическое программирование.

Функциональный стиль программирования сложился в практике решения задач символьной обработки данных в предположении, что любая информация для компьютерной обработки может быть сведена к символьной (существование аналоговых методов принципиально не противоречит этой гипотезе). Слово «символ» здесь близко понятию «знак» в знаковых системах. Информация представляется символами, смысл которых может быть восстановлен по заранее известным правилам.

Функциональное программирование рассматривает процесс обработки данных как композицию их отображений с помощью универсальных функций. Программа при таком подходе – не более чем одна из разновидностей данных. Функциональное программирование сумело преодолеть синтаксический разнобой независимо развиваемых средств и методов организации информационных процессов. Это удалось благодаря нацеленности на проявление и ортогонализацию семантических подсистем в организации программируемых процессов. Не менее важна развиваемость представления унифицируемых структур данных и комплектов функциональных объектов. Все это позволяет языки функционального программирования рассматривать как средство сопряжения разнородных конструкций. На их основе можно осуществлять любые интегрированные построения, представимые в машинно-независимом стиле.

Языки функционального программирования (ЯФП) используют гибкие списки и абстрактные атомы. ЯФП нацелены на полный контроль типов значений при исполнении программ, допускающих динамический анализ и управление с откладыванием вычислений, автоматизацию повторного использования памяти – «сборки мусора».

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

Функциональное программирование отличается от большинства подходов к программированию тремя важными принципами.

1. Природа данных. Все данные представляются в форме символьных выражений. Данные реализуются как древообразные структуры, что позволяет локализовывать любые важные подвыражения. Система программирования с такими структурами обычно использует для их хранения всю доступную память, поэтому программист может быть освобожден от распределения памяти под отдельные блоки данных.

2. Самоописание обработки символьных выражений. Важная особенность функционального программирования состоит в том, что описание способов обработки данных представляется программами, рассматриваемыми как символьные данные. Программы строятся из рекурсивных функций. Определения и вызовы этих функций, как и любая информация, могут обрабатываться как обычные данные, получаться в процессе вычислений и преобразовываться как значения.

3. Подобие машинным языкам. Система функционального программирования допускает, что программа может интерпретировать и/или компилировать программы, представленные в виде структур данных. Это сближает методы функционального программирования с методами низкоуровневого программирования и отличает от традиционной методики применения языков высокого уровня. Не все языки функционального программирования в полной мере допускают эту возможность, но для языка Lisp она характерна. В принципе, такая возможность достижима на любом стандартном языке, но так делать не принято [4, c. 100].

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

Исследовательская и проектная работы обычно проходят фазу поиска практичного решения. Логическое программирование (ЛП) для поддержки этой фазы предлагает важное отступление от концепции определения подпрограмм, процедур и функций. В качестве укрупненного определения действий допускаются варианты, равноправно выбираемые из конечного множества так называемых «клауз». По мере изучения задачи это множество можно пополнять в произвольном порядке. Именно эта идея составляет одну из привлекательных особенностей ЛП, выделившегося в самостоятельную парадигму. Равноправие не распространяется лишь на тупиковую ситуацию, когда ни один предложенный вариант не приводит к целевому результату.

Парадигма логического программирования использует идею автоматического вывода информации на основе заданных фактов и правил. Логическое программирование основано на теории и аппарате формальной логики. Написанная согласно формальной логике программа является множеством логических форм, представляющих факты и правила относительно некоторой предметной области. Основным языком логического программирования признан Prolog, хотя известны и другие – Planner, ASP и Datalog. Во всех таких языках правила имеют форму клауз:

H :- B11, …, BNn,

понимаемую как логическое следование

if (B11 and … and BN) then H или B11 & … & BN → H

H называют головой правила, а B11, …, BN – телом. Факты – это правила без тела. Различают атомарные и составные клаузы. Предикаты, образующие тело, могут быть выражены в стиле сопоставления с образцом. Важный механизм – использование отрицаний в теле клауз, что приводит к немонотонной логике. Логика программ может использовать процедурный стиль при вычислении целей:

to solve H, solve B1, and ... and solve Bn.

В целом следует отметить, что декларативный подход к пониманию программ требует от программиста систематической проверки корректности. Более того, используются преобразования логических программ в их более эффективные эквиваленты, что сближает ЛП с макротехникой. Для повышения эффективности программ программисту следует знать особенности поведения механизма вычислений и границы вычислимости используемых выражений.

3 Характеристика популярных языков программирования высокого уровня

3.1 Java

Для оценки популярности языков программирования за определенный период используется индекс TIOBE - индекс, оценивающий популярность языков программирования, основываясь на количестве поисковых запросов, содержащих название языка [6].

Одни языки становятся более популярными, другие – утрачивают популярность. Динамика индекса с конца 2011 до середины 2017 года представлена на рис. 4.

Рисунок 4 – Динамика индекса TIOBE [16]

Несмотря на определенное снижение популярности, лидирующие позиции сохраняет язык Java.

История Java восходит к 1991 году, когда группа инженеров из компании Sun под руководством Патрика Нотона (Patrick Naughton) и члена Совета директоров Джеймса Гослинга (James Gosling) занялась разработкой языка, который можно было бы использовать для программирования бытовых устройств, например, контроллеров для переключения каналов кабельного телевидения. Поскольку такие устройства не потребляют много энергии и не имеют больших микросхем памяти, язык должен был генерировать очень компактные программы. Кроме того, поскольку разные производители могут выбирать разные центральные процессоры, было важно не ориентироваться только на какую-то одну архитектуру компьютеров [3].

Технология Java – это объектно-ориентированная, платформонезависимая, многопоточная среда программирования.

Язык Java стал основой для «умных» Web - и сетевых сервисов, позволил надежно и безопасно наращивать информационную структуру предприятия без зависимости от отдельной платформы. Все виды систем могут взаимодействовать друг с другом – начиная от мобильных устройств и заканчивая суперкомпьютерами – независимо от их аппаратной платформы и системного программного обеспечения. Java-приложение можно исполнять практически на любом типе машин – персональном компьютере, компьютере Мак или даже на сотовом телефоне.

Когда программный продукт, написанный на языке программирования Java, компилируется с использованием Java-компилятора, получается байт-код, который может интерпретироваться на любой платформе, где установлена виртуальная машина Java. Это означает, что нет необходимости в портировании программ, то есть их перевода на язык, понятный конкретному компьютеру [14, с. 187].

Архитектурная независимость - лишь составная часть переносимости. В отличие от С или С++ в Java не существует понятия "зависимости от реализации", когда речь идет о размерности базовых типов. Форматы типов данных и операции над ними четко определены. Тем самым, программы остаются неизменными на любой платформе - не существует несовместимости типов данных на аппаратных и программных архитектурах.

Архитектурная независимость и переносимость программного обеспечения Java обеспечивается виртуальной машиной Java (Java Virtual Mashine - JVM) - абстрактной машиной, для которой компилятор Java генерирует код. Специальные реализации JVM для конкретных аппаратных и программных платформ предоставляют уже конкретную виртуальную машину. JVM базируется на стандарте интерфейса переносимых операционных систем (POSIX).

Java разработан для оперирования в распределенных средах, это означает, что на первом плане должны стоять вопросы безопасности. Средства безопасности, встроенные в язык, и система исполнения Java позволяют создавать приложения, на которые невозможно «напасть» извне. В сетевых средах приложения, написанные на Java, защищены от вторжения неавторизованного кода, пытающегося внедрить вирус или разрушить файловую систему.

Язык программирования Java с самого начала проектировался как объектно-ориентированный. Задачам распределенных систем клиент-сервер отвечает объектно-ориентированная парадигма: использование концепций инкапсуляции, наследования и полиморфизма. Язык Java предоставляет ясную и действенную объектно-ориентированную платформу разработки. Программисты на Java могут использовать стандартные библиотеки объектов, обеспечивающие работу с устройствами ввода/вывода, сетевые функции, методы создания графических пользовательских интерфейсов.

Платформа Java разработана для создания высоконадежного прикладного программного обеспечения. Большое внимание уделено проверке программ на этапе компиляции, за которой следует второй уровень - динамическая проверка (на этапе выполнения). Модель управления памятью предельно проста: объекты создаются с помощью оператора new. В Java, в отличие от С++, механизм указателей исключает возможность прямой записи в память и порчи данных: при работе с указателями операции строго типизированы, отсутствуют арифметические операции над указателями. Работа с массивами находится под контролем управляющей системы. Существует автоматическая сборка мусора. Данная модель управления памятью исключает целый класс ошибок, так часто возникающих у программистов на С и С++. Программы на Java можно писать, будучи уверенным в том, что машина не «повиснет» из-за ошибок при работе с динамически выделенной памятью.

В целом можно отметить, что, если описанные выше характеристики рассматривать по отдельности, то их можно найти во многих программных платформах. Радикальное новшество заключается в способе, предлагаемом Java и системой выполнения, который сочетает в себе все характеристики для предоставления гибкой и мощной системы программирования. Разработка приложений на Java приводит к получению программного обеспечения, которое: переносимо на разные архитектуры, операционные системы и графические пользовательские интерфейсы; безопасно; высокопроизводительно. Благодаря Java работа по разработке программного обеспечения значительно упрощается, все старания направлены на достижение конечной цели: вовремя получить передовой продукт, опирающийся на солидную основу Java.

3.2 С/С++

Язык С, созданный Денисом Ритчи в начале 1970-х годов в Bell Laboratory американской корпорации AT&T, является одним из универсальных языков программирования. Язык С считается языком системного программирования, хотя он удобен и для написания прикладных программ. Среди преимуществ языка С следует отметить переносимость программ на компьютеры различной архитектуры и из одной операционной системы в другую, лаконичность записи алгоритмов, логическую стройность программ, а также возможность получить программный код, сравнимый по скорости выполнения с программами, написанными на языке ассемблера. Последнее связано с тем, что хотя С является языком высокого уровня, имеющим полный набор конструкций структурного программирования, он также обладает набором низкоуровневых средств, обеспечивающих доступ к аппаратным средствам компьютера. С 1989 года язык С регламентируется стандартом Американского института национальных стандартов ANSI С. В настоящее время, кроме стандарта ANSI C разработан международный стандарт ISO C (International Standard Organization C) [9, c. 8].

Бьерн Страуструп высвободил объектно-ориентированный потенциал С путем перенесения возможностей классов Simula 67 в С. Первоначально новый язык носил имя «С с классами» и только потом стал называться C++. Язык C++ достиг популярности, будучи разработанным в Bell Labs, позже он был перенесен в другие индустрии и корпорации. Сегодня это один из наиболее популярных языков программирования в мире. C++ наследует как хорошие, так и плохие стороны С.

В книге «Дизайн и эволюция C++» Бьёрн Страуструп описывает принципы, которых он придерживался при проектировании C++. Эти принципы объясняют, почему C++ именно такой, какой он есть. Некоторые из них:

  • получить универсальный язык со статическими типами данных, эффективностью и переносимостью языка C;
  • непосредственно и всесторонне поддерживать множество стилей программирования, в том числе процедурное программирование, абстракцию данных, объектно-ориентированное программирование и обобщённое программирование;
  • дать программисту свободу выбора, даже если это даст ему возможность выбирать неправильно;
  • максимально сохранить совместимость с C, тем самым делая возможным лёгкий переход от программирования на C;
  • избежать разночтений между C и C++: любая конструкция, допустимая в обоих языках, должна в каждом из них обозначать одно и то же и приводить к одному и тому же поведению программы;
  • избегать особенностей, которые зависят от платформы или не являются универсальными;
  • «не платить за то, что не используется» — никакое языковое средство не должно приводить к снижению производительности программ, не использующих его;
  • не требовать слишком усложнённой среды программирования [12, с. 17].

В языке С++ полностью поддерживаются принципы объектно-ориентированного программирования, включая три кита, на которых оно стоит: инкапсуляцию, наследование и полиморфизм. Инкапсуляция в С++ поддерживается посредством создания нестандартных (пользовательских) типов данных, называемых классами. Язык С++ поддерживает наследование. Это значит, что можно объявить новый тип данных (класс), который является расширением существующего.

Итак, хотя язык программирования С++ справедливо называют продолжением С и любая работоспособная программа на языке С будет поддерживаться компилятором С++, при переходе от С к С++ был сделан весьма существенный скачок. Язык С++ выигрывал от своего родства с языком С в течение многих лет, поскольку многие программисты обнаружили, что для того, чтобы в полной мере воспользоваться преимуществами языка С++, им нужно отказаться от некоторых своих прежних знаний и приобрести новые, а именно: изучить новый способ концептуальности и решения проблем программирования.

Заключение

Язык программирования служит двум взаимосвязанным целям: предоставляет программисту аппарат для задания действий, которые должны быть выполнены компьютером, и формирует концепции, которыми пользуется программист, продумывая будущий алгоритм. Первой цели идеально отвечает язык, который настолько «близок к ЭВМ», что всеми основными машинными объектами можно легко и просто оперировать достаточно очевидным для программиста образом. Второй же цели идеально отвечает язык, который настолько близок к решаемой задаче, что концепции ее решения можно выражать прямо и коротко. Основное различие между языками высокого и низкого уровней состоит в повышении эффективности труда программиста благодаря абстрагированию от конкретных деталей аппаратного обеспечения. Одна инструкция (оператор) языка высокого уровня соответствует целой последовательности из нескольких низкоуровневых инструк­ций, или команд.

Языки программирования могут классифицироваться по различным основаниям, общепринятой системы в этом вопросе не выработано.

Можно выделить два принципиально разных способа реализации языков программирования: компиляция и интерпретация. Распространено заблуждение, согласно которому способ реализации является присущим конкретному языку свойством. В действительности, это деление до определенной степени условно. Компилятор переводит программу на машинный язык сразу и целиком, создавая при этом отдельную программу, а интерпретатор переводит на машинный язык прямо во время исполнения программы.

Языки программирования ориентированы на те или иные парадигмы, и, соответственно, представляют специальные средства для удобства ее реализации. Можно выделить две важнейших парадигмы – императивную и декларативную.

Программа, написанная на императивном языке, явно указывает способ получения желаемого результата, не определяя при этом его ожидаемых свойств. Процедура получения желаемого результата имеет вид последовательности операций, поэтому для императивных языков характерно указание порядка выполнения операторов. В рамках императивной парадигмы выделяют структурное, процедурное, модульное и объектно-ориентированное программирование.

Декларативные языки – это класс языков программирования, программа на которых задаёт связи и отношения между объектами и величинами и не определяет последовательность выполнения действий. Декларативные языки могут реализовать функциональный или логический подход.

Для оценки популярности языков программирования за определенный период используется индекс TIOBE - индекс, оценивающий популярность языков программирования, основываясь на количестве поисковых запросов, содержащих название языка. Одни языки становятся более популярными, другие – утрачивают популярность. В 2017 году самыми популярными являются Java, C, C++.

Список использованных источников

  1. Бекман, И.Н. Компьютерные науки: курс лекций / И.Н. Бекман. – М.: МГУ, 2016. – 272 с.
  2. Бродский, Ю.И. Лекции по математическому и имитационному моделированию / Ю.И. Бродский. – М.: Директ-Медиа, 2015. – 240 с.
  3. Владимирцев, С. Java-учебник / С. Владимирцев // Изучаем Java [Электронный ресурс]. – Режим доступа: http://java-study.ru/java-uchebnik.html
  4. Городняя, Л.В. Парадигма программирования: курс лекций / Л.В. Городняя. – Новосибирск: РИЦ НГУ, 2015. – 206 с.
  5. Давыдова, Н.А. Программирование: учебное пособие / Н.А. Давыдова, Е.В. Боровская. – М.: БИНОМ, 2015. – 232 с.
  6. Джансен, П. Интервью про индекс TIOBE / П. Джансен // RSDN [Электронный ресурс]. – Режим доступа: http://rsdn.org/forum/philosophy/2315566.hot
  7. «Забытые» парадигмы программирования // Хабрахабр [Электронный ресурс]. – Режим доступа: https://habrahabr.ru/post/223253/
  8. Кудрявцева, И.А. Классификация парадигм программирования в контексте теоретического программирования / И.А. Кудрявцева // Известия Российского государственного педагогического университета им. А.И. Герцена. – 2015. - №173. – С. 78-88.
  9. Курсков, С.Ю. Введение в язык С / С.Ю. Курсков. – Тверь: ТГУ, 2012. – 406 с.
  10. Лавров, Д.Н. От императивного к объектно-ориентированному программированию вместе с Java и NetBeans: объектная декомпозиция и инкапсуляция / Д.Н. Лавров // Математические структуры и моделирование. – 2009. - №20. – С. 178-190.
  11. Максаков, С.А. Язык Pascal / С.А. Максаков. – СПб.: КомпАС, 2014. – 316 с.
  12. Страуструп, Б. Дизайн и эволюция С++ / Б.Страуструп. – СПб.: Питер, 2007. – 445 с.
  13. Терехов, А.А. Разработка компиляторов на платформе .NET: курс лекций / А.А.Терехов, Н. Вояковская, Д. Булычев, А. Москаль. – СПб.: СПбГУ, 2016. – 196 с.
  14. Фаткуллин, М.Р. Сравнительная характеристика сетевых языков программирования Java и Perl / М.Р. Фаткуллин, А.Ф. Антипин // Сборник научных статей «Ломоносовские чтения на Алтае», 2013. – С. 186-191
  15. Эккель, Б. Философия Java / Б. Эккель. – СПб.: Питер, 2016. – 1168 с.
  16. TIOBE Index for July 2017 // TIOBE [Электронный ресурс]. – Режим доступа: https://www.tiobe.com/tiobe-index/