Лекция 3. Языки программирования Вопросы лекции 1. История развития языков программирования 2. Парадигмы языков программирования 3. Классификация языков программирования 4. Транслятор, компилятор и интерпретатор Вопрос 1. История развития языков программирования Программа – это набор последовательных команд, то есть алгоритм для объекта(исполнителя), который должен их выполнить для достижения определенной цели. Необходимость в особых языках связана с тем, что машины не понимают естественные языка. Инструкции для машин пишут на языках программирования, которые характеризуются формальностью, то есть синтаксической однозначностью и ограниченностью. Первые программы писались на машинном языке, так как для ЭВМ того времени развитого программного обеспечения не существовало, машинный язык – единственный способ взаимодействия с аппаратным обеспечением компьютера. Программа – это набор инструкций для определенного исполнителя. Язык программирования – это формальный язык, предназначенный для записи программ (обычно для ЭВМ). Стремление человека оперировать словами, а не цифрами привело к появлению ассемблеров. Это языки, в которых вместо численного обозначения команд и областей памяти используются словесно-буквенные. Основные этапы исторического развития языков программировани я 1. Машинный язык 2. Ассемблер Проблема: машина не в состоянии понимать слова. Начиная со времен ассемблеров, под каждый язык программирования создаются трансляторы. Транслятор – специальная программа, преобразующая программный код с языка программирования в машинный код. 1. Машинный язык 2. Ассемблер Транслятор После ассемблеров появляются языки высокого уровня. Для таких языков потребовалось разрабатывать более сложные трансляторы. Основное отличие в том, что ассемблеры привязаны к своим типам машин, языки высокого уровня обладают переносимостью. Более сложные трансляторы делятся на компиляторы и интерпретаторы. Компилятор – читает всю программу и преобразует ее в объектный код, который способен напрямую выполнятся компьютером. Интерпретатор – читает исходный код программы построчно и выполняет инструкции содержащиеся в текущей строчке, потом переходит к следующей. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня Следующим шагом было появление объектно-ориентированных языков, что в первую очередь связано с усложнением разрабатываемых программ. С помощью таких языков программист управляет виртуальными объектами. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Из-за увеличения сфер использования ЭВМ появились и другие языки для отдельных разработок в новых сферах: -экономическое направление (Кобол); -Снобол – обрабатывает алгоритмы, связанные с текстами; -Лисп – работает на основании алгоритмов для обработки символов, используется для формирования искусственного интеллекта. В 1968 году запустили конкурс лучшего языка программирования для начала карьерного пути. Им стал Алгол-68, но он остался малоизвестным. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Специально для участия в конкурсе был создан Паскаль (разработчик – Никлаус Вирт). Не смотря на изначальную разработку с целью обучения студентов, Паскаль получил широкое распространение и активно развивался. Для обучения детей в школах был создан Лого (Самуэль Пайперт). Однако, в школах стал преподаваться Бейсик, легко взаимодействующий с ЭВМ в качестве прямого диалога. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Развитие возможностей вычислительного оборудования привело к необходимости написания емких программ для управления ЭВМ. В 70-х годах начал активно использоваться язык Си. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Внедрение функционального программирование повлекло за собой создание Пролога, задачи которого сводились к анализу и взаимодействую с человеческими языками. Логика приложения формальна, она оптимально подходила для автоматического решения задач и теорем. В 80-х годах разработали язык Ада. Он расширил классическое понимание свойств языка того периода. Ада могла решать задачи в режиме реального времени и моделировать независимые решения. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Информатика в современном мире развивается в нескольких ключевых направлениях: 1. Процедурное появилось в период активного развития компьютеров и других вычислительных устройств. В процедурных направлениях присутствуют выраженные описания действий, необходимых к выполнению. 2. Структурные. В них используется один оператор для записи цельных алгоритмов: циклов, функций, ветвлений и остального. 3. Операционные. Применяют несколько различных действий. 4. Непроцедурные. Языки программирования имеют декларативную структуру. Основная задача – создание возможностей для построения высокоинтеллектуальных машин. 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Непроцедурные также разделяются на: 1. Функциональные. Программа выполняет исчисление определенной функции, которая берет за основу другие относительно простые алгоритмы и более простые задачи. В основе функционального направления используется основной элемент – рекурсия. 2. Логические. Программа не требует описание действий, ее основу составляют соотношения данных и их значения. Только после расчета можно получать ответы на вопросы. В программе отсутствует метод или порядок обнаружения ответа, он неявным образом устанавливается языком. 3. Объектно-ориентированные языки. Не нуждаются в описании четкой последовательности манипуляций для 1. Машинный язык 2. Ассемблер Транслятор Компилятор Интерпретатор 3. Языки высокого уровня 4. Объектноориентированные языки Вопрос 2. Парадигмы языков программирования Парадигма программирования - это устоявшаяся система взглядов, определяющая стиль программирования, некоторый цельный набор идей и рекомендаций, определяющих стиль написания программ Парадигмы программирования: процедурная (императивная) объектно-ориентированная функциональная логическая Процедурное программирование Процедурное (императивное) программирование является отражением архитектуры традиционных ЭВМ, которая была предложена фон Нейманом в 1940-х годах. Программа на процедурном языке программирования состоит из последовательности операторов (инструкций), задающих процедуру решения задачи. Основным является оператор присваивания, служащий для изменения содержимого областей памяти. Концепция памяти как хранилища значений, содержимое которого может обновляться операторами программы, является фундаментальной в императивном программировании. Выполнение программы сводится к последовательному выполнению операторов с целью преобразования исходного состояния памяти, то есть значений исходных данных, в заключительное, то есть в результаты. Таким образом, с точки зрения программиста имеются программа и память, причем первая последовательно обновляет содержимое последней. Примеры процедурных языков: FORTRAN, Pascal, C, Ada. Процедурное программирование: структурное (C, Pascal), операционное (FORTRAN, BASIC). Объектно-ориентированное программирование Объектно-ориентированное программирование (ООП) - это парадигма программирования, при использовании которой главными элементами программ являются объекты. В языках программирования понятие объекта реализовано как совокупность свойств (структур данных, характерных для данного объекта), методов их обработки (подпрограмм изменения их свойств) и событий, на которые данный объект может реагировать и, которые приводят, как правило, к изменению свойств объекта. Инкапсуляция - объединение данных и свойственных им процедур обработки в одном объекте. Класс (фундаментальное понятие парадигмы ООП) - это шаблон, на основе которого может быть создан конкретный программный объект, он описывает свойства и методы, определяющие поведение объектов этого класса. Каждый конкретный объект, имеющий структуру этого класса, называется экземпляром класса. Наследование предусматривает создание новых классов на базе существующих и позволяет классу-потомку иметь (наследовать) все свойства класса-родителя. Полиморфизм - возможность объектов с одинаковой спецификацией иметь различную реализацию (использовать различные методы обработки для разных объектов). Модульность: объекты заключают в себе полное определение их характеристик, никакие определения методов и свойств не должны располагаться вне его, это делает возможным свободное копирование и внедрение одного объекта в другие. Примеры объектно-ориентированных языков: C++, Java, Object Pascal, Python. Функциональное программирование Функциональное программирование - парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в их математическом понимании (в отличие от функций как подпрограмм и процессу вычислений как последовательному изменению состояний в процедурном программировании). Функциональное программирование - это способ составления программ, в которых единственным действием является вызов функции. Роль основной конструкции в функциональных языках играет выражение, к которым относятся скалярные константы, структурированные объекты, функции, тела функций и вызовы функций. Программа представляет собой совокупность описаний функций и выражения, которые необходимо вычислить. Вычисление выражения описывается как комбинация вызовов функций того же или более низкого уровня (часто рекурсивных). Каждая следующая функция в этой комбинации описывается аналогичным образом, до тех пор, пока описание не сведётся к предопределённым функциям, вычисление которых считается заданным. В отличие от императивного стиля, описывающего шаги, ведущие к достижению цели, функциональный стиль описывает математические отношения между данными и целью. Функциональное программирование основано на вычислении результатов функций в зависимости от исходных данных и результатов выполнения других функций и не предполагает явного хранения состояния программы (и переменных в памяти). Операторы присваивания отсутствуют (изменения состояния переменных не производится), вследствие чего переменные обозначают не области памяти, а объекты программы, что полностью соответствует понятию переменной в математике. Примеры функциональных языков: Lisp, Haskell, ML. Логическое программирование Логическое программирование - парадигма программирования, базирующаяся на автоматическом доказательстве теорем с использованием механизмов логического вывода информации на основе заданных фактов и правил вывода. Центральным понятием в логическом программировании является отношение. Программа представляет собой совокупность определений отношений между объектами (в терминах условий или ограничений) и цели (запроса). При этом нужно только специфицировать факты, на которых основывается алгоритм, а не определять последовательность шагов, которые требуется выполнить. Программа строится из последовательности фактов и правил, затем формулируется утверждение, которое язык пытается доказать с помощью правил. Язык сам ищет решение с помощью методов поиска и сопоставления, которые в нем заложены. Логические программы не отличаются высоким быстродействием, так как процесс их выполнения сводится к построению прямых и обратных цепочек рассуждений разнообразными методами поиска. Примеры логических языков: Prolog, Planner, Popler, Mercury. Пример программы на языке Prolog, вычисляющей числа Фибоначчи: fib(0) = 1, fib(1) = 1 fib(n) = fib(n-1) + fib(n-2) 3. Классификация программирования языков Все языки программирования можно разделить на группы: 1) Языки низкого уровня. 2) Языки высокого уровня. Языки низкого уровня – это средство записи программ компьютеру с помощью машинных команд. Языки программирования высокого уровня допускают описание задачи в наглядном, легко воспринимаемом виде. Их отличительной особенностью является ориентация не на систему команд, а на систему инструкций, характерных для записи алгоритмов определенного класса. Языки же высокого уровня имитируют естественные языки. Языки высокого уровня делятся на: • алгоритмические (Basic, Pascal, C и др.), которые предназначены для однозначного описания алгоритмов; • логические (Prolog, Lisp и др.), которые ориентированы не на разработку алгоритма решения задачи, а на систематическое и формализованное описание задачи с тем, чтобы решение следовало из составленного описания. • объектно-ориентированные (Object Pascal, C++, Java и др.), в основе которых лежит понятие объекта, сочетающего в себе данные и действия над нами. Языки можно также классифицировать по следующим признакам. 1. По степени ориентации на специфические возможности ЭВМ ЯП делятся на: · машинно-зависимые; · машинно-независимые. К машинно-зависимым ЯП относятся машинные языки, ассемблеры и автокоды, которые используются в системном программировании. Программа на машинно-зависимом ЯП может выполняться только на ЭВМ данного типа. Программа на машинно-независимом ЯП после трансляции на машинный язык становится машинно-зависимой. Этот признак ЯП определяет мобильность получаемых программ (возможность переноса на ЭВМ другого типа). По степени ориентации на решение определенного класса задач: · проблемно-ориентированные; · универсальные. Проблемно-ориентированные языки принято разделять на три группы: 1) Внешние. Используют синтаксис, полностью отличный от синтаксиса языка основного приложения. Примеры: little languages в Unix, регулярные выражения, SQL, PostScript, awk, HTML и т. д. 2) Внутренние. Являются расширением исходного языка или его ограничителем. К классическим примерам внутренних проблемно-ориентированных языков можно отнести: LISP, Ruby и Jruby 3) Инструментальные средства языка Примеры проблемно-ориентированных задач: Фортран. Был разработан специально для программирования научных задач Кобол. В основном предназначался для решения экономических задач. GPSS. Ориентирована на моделирование систем с помощью событий. В терминах этого языка легко описывается класс моделей массового обслуживания Универсальные языки программирования — это те, которые помогают в любой ситуации и сочетают простоту изучения и функциональность. По возможности дополнения новыми типами данных и операциями: · расширяемые; · нерасширяемые. Расширяемые языки программирования — это языки, синтаксис и семантика которых не фиксированы, а могут быть изменены в зависимости от потребностей программиста. Чтобы язык был расширяемым, в нём должны присутствовать следующие элементы: - механизм расширения без изменения компилятора, одинаково поддерживаемый всеми компиляторами; - конструкции для расширения и манипуляции с деревом кода, такие как макросы или квазицитирование; - мощные средства написания расширений. Примеры: Lisp; Nemerle; Seed7 и т.д. Нерасширяемые языки программирования — это те, которые не поддерживают возможностей для расширения без изменения в ядре компилятора и имеют статичный функционал. (Пример Basic) По возможности управления реальными объектами и процессами: · языки систем реального времени; · языки систем условного времени. Программные системы, обеспечивающие работу систем реального времени (системы промышленной автоматизации, робототехнические устройства, встроенные в технологическое оборудование цифровые устройства управления и т.п., осуществляющих обработку данных, поступающих с физических объектов в реальном времени, с целью контроля и управления их состоянием), составляют особый класс программных приложений, называемых приложениями реального времени, или сокращённо – ПРВ Для разработки систем реального времени используется ассемблер, C, C++ который обеспечивает получение наивысшей производительности, прямой доступ к оборудованию, возможность вызова любых процедур на других языках. Языки систем условного времени позволяют управлять реальными объектами и процессами с помощью условных операторов. По способу получения результата: · процедурные; · непроцедурные. Процедурные языки программирования — это языки, в которых код можно разделить на подпрограммы (процедуры и функции) Основная особенность процедурных языков — их императивность, что означает создание чёткого набора последовательных инструкций, которые должен поочередно выполнять компьютер Примеры процедурных языков программирования: Fortran, COBOL, Algol, C, Ada, C. Непроцедурные языки программирования — это языки, при использовании которых в программе в явном виде указывается, какими свойствами должен обладать результат, но не говорится, каким способом он должен быть получен. Компьютер уже на своей стороне самостоятельно выстраивает необходимый алгоритм выполнения. Непроцедурные языки программирования универсальны для использования в разных процессах, так как не требуют привязки к определённой архитектуре. Для достижений этой цели применяются специальные переводчики-трансляторы. По типу решаемых задач: · языки системного программирования; · языки прикладного программирования. Языки системного программирования — это языки, которые используются для создания дополнительных программ, помогающих разработчикам и системным администраторам. Вот некоторые из таких языков: C — используется для создания операционных систем, драйверов устройств и базовых программных компонентов. C++ — универсальный выбор для системного программирования, позволяющий создавать сложные, но удобные для сопровождения кодовые базы. Rust — обеспечивает надёжную защиту от целого ряда распространённых ошибок программирования. Прикладной язык программирования построен из функций, применяемых к аргументам. Языки прикладного программирования представляют собой инструменты, которые используются для создания прикладных программ – программ, предназначенных для решения конкретных задач. Эти языки обладают различными особенностями, которые делают их подходящими для различных областей разработки. Вопрос 4. Транслятор, компилятор и интерпретатор Неотъемлемая часть современных ЭВМ – системы программного обеспечения, являющиеся логическим продолжением логических средств ЭВМ, расширяющим возможности аппаратуры и сферу их использования. Система программного обеспечения, являясь посредником между человеком и техническими устройствами машины, автоматизирует выполнение тех или иных функций в зависимости от профиля специалистов и режимов их взаимодействия с ЭВМ. Основное назначение программного обеспечения – повышение эффективности труда пользователя, а также увеличение пропускной способности ЭВМ посредством сокращения времени и затрат на подготовку и выполнение программ. Программное обеспечение ЭВМ можно подразделить на общее и специальное программное обеспечение. Общее программное обеспечение реализует функции, связанные с работой ЭВМ, и включает в себя системы программирования, операционные системы, комплекс программ технического обслуживания. Специальное программное обеспечение включает в себя пакеты прикладных программ, которые проблемно ориентированы на решение вполне определенного класса задач. Системой программирования называются комплексы программ и прочих средств, предназначенных для разработки и их эксплуатации на конкретном языке программирования для конкретного вида ЭВМ. Система программирования освобождает проблемного пользователя или прикладного программиста от необходимости написания программ решения своих задач на неудобном для него языке машинных команд, и предоставляют им возможность использовать специальные языки более высокого уровня. Для каждого из таких языков, называемых входными или исходными, система программирования имеет программу, осуществляющую автоматический перевод (трансляцию) текстов программы с входного языка на язык машины. Трансляторы Трансля́тор – это Программа или техническое средство, выполняющее трансляцию программы. Машинная программа, которая транслирует с одного языка на другой и, в частности, с одного языка программирования на другой. Обрабатывающая программа, предназначенная для преобразования исходной программы в объектный модуль. Транслятор обычно выполняет также диагностику ошибок, формирует словари идентификаторов, выдаёт для печати тексты программы и т.д. Трансляция программы — преобразование программы, представленной на одном из языков программирования, в программу на другом языке и, в определённом смысле, равносильную первой. Понятие трансляции относится не только к языкам программирования, но и к другим компьютерным языкам, вроде языков разметки, аналогичных HTML, и к естественным языкам, вроде английского или русского. Цель трансляции — преобразовать текст с одного языка на другой, который понятен адресату текста. В случае программ-трансляторов, адресатом является техническое устройство (процессор) или программа-интерпретатор. Трансляторы реализуются в виде компиляторов или интерпретаторов. Компилятор Программа или техническое средство, выполняющее компиляцию. Машинная программа, используемая для компиляции. Транслятор, выполняющий преобразование программы, составленной на исходном языке, в объектный модуль. Программа, переводящая текст программы на языке высокого уровня в эквивалентную программу на машинном языке. Программа, предназначенная для трансляции высокоуровневого языка в абсолютный код или, иногда, в язык ассемблера. Входной информацией для компилятора (исходный код) является описание алгоритма или программа на проблемно-ориентированном языке, а на выходе компилятора — эквивалентное описание алгоритма на машинно-ориентированном языке (объектный код). Компиляция Трансляция программы на язык, близкий к машинному. Трансляция программы, составленной на исходном языке, в объектный модуль. Осуществляется компилятором. Структура компилятора Процесс компиляции состоит из следующих этапов: 1) Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем. 2) Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в дерево разбора. 3) Семантический анализ. Дерево разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным деревом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки. 4) Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом. 5) Генерация кода. Из промежуточного представления порождается код на целевом языке. Недостатки и преимущества использования компиляторов Достоинство: программа компилируется один раз и при каждом выполнении не требуется дополнительных преобразований. Соответственно, не требуется наличие компилятора на целевой машине, для которой компилируется программа. Недостаток: Отдельный этап компиляции замедляет написание и отладку и затрудняет исполнение небольших, несложных или разовых программ. Динамическая компиляция (Just-in-time compilation - JIT, компиляция «на лету») По мере увеличения ресурсов компьютеров и расширения гетерогенных сетей (в том числе Интернета), связывающих компьютеры разных типов и архитектур, выделился новый вид интерпретации, при котором исходный (или промежуточный) код компилируется в машинный код непосредственно во время исполнения, «на лету». Уже скомпилированные участки кода кэшируются, чтобы при повторном обращении к ним они сразу получали управление, без перекомпиляции. Этот подход получил название динамической компиляции. • Достоинство - скорость интерпретации программ становится сравнимой со скоростью исполнения программ в обычных компилируемых языках, при этом сама программа хранится и распространяется в единственном виде, независимом от целевых платформ. • Недостаток - бо́льшая сложность реализации и бо́льшие требования к ресурсам, чем в случае простых компиляторов или чистых интерпретаторов. Интерпретатор Интерпрета́тор (языка программирования) – • Программа или техническое средство, выполняющее интерпретацию. • Вид транслятора, осуществляющего пооператорную (покомандную) обработку и выполнение исходной программы или запроса (в отличие от компилятора, транслирующего всю программу без её выполнения). • Программа (иногда аппаратное средство), анализирующая команды или операторы программы и тут же выполняющая их. • Языковый процессор, который построчно анализирует исходную программу и одновременно выполняет предписанные действия, а не формирует на машинном языке скомпилированную программу, которая выполняется впоследствии. Простой интерпретатор анализирует и тут же выполняет (собственно интерпретация) программу покомандно (или построчно), по мере поступления её исходного кода на вход интерпретатора. Достоинство такого подхода является мгновенная реакция. Недостаток — такой интерпретатор обнаруживает ошибки в тексте программы только при попытке выполнения команды (или строки) с ошибкой. Алгоритм работы простого интерпретатора 1. прочитать инструкцию; 2. проанализировать инструкцию и определить соответствующие действия; 3. выполнить соответствующие действия; 4. если не достигнуто условие завершения программы, прочитать следующую инструкцию и перейти к пункту 2. Интерпретатор компилирующего типа — это система из компилятора, переводящего исходный код программы в промежуточное представление, например, в байт-код или p-код, и собственно интерпретатора, который выполняет полученный промежуточный код (так называемая виртуальная машина). Достоинство таких систем - большее быстродействие выполнения программ (за счёт выноса анализа исходного кода в отдельный, разовый проход, и минимизации этого анализа в интерпретаторе). Недостатки — большее требование к ресурсам и требование на корректность исходного кода. Применяется в таких языках, как Java, PHP, Python, Perl (используется байт-код). Некоторые интерпретаторы (например, для языков Лисп, Scheme, Python, Бейсик и других) могут работать в режиме диалога или так называемого цикла чтения-вычисления-печати (англ. read-eval-print loop, REPL). В таком режиме интерпретатор считывает законченную конструкцию языка , выполняет её, печатает результаты, после чего переходит к ожиданию ввода пользователем следующей конструкции. Уникальным является язык Forth, который способен работать как в режиме интерпретации, так и компиляции входных данных, позволяя переключаться между этими режимами в произвольный момент, как во время трансляции исходного кода, так и во время работы программ. Достоинства и недостатки интерпретаторов Достоинства Большая переносимость интерпретируемых программ — программа будет работать на любой платформе, на которой есть соответствующий интерпретатор. Как правило, более совершенные и наглядные средства диагностики ошибок в исходных кодах. Упрощение отладки исходных кодов программ. Меньшие размеры кода по сравнению с машинным кодом, полученным после обычных компиляторов. Недостатки Интерпретируемая программа не может выполняться отдельно без программыинтерпретатора. Сам интерпретатор при этом может быть очень компактным. Интерпретируемая программа выполняется медленнее, поскольку промежуточный анализ исходного кода и планирование его выполнения требуют дополнительного времени в сравнении с непосредственным исполнением машинного кода, в который мог бы быть скомпилирован исходный код. Практически отсутствует оптимизация кода, что приводит к дополнительным потерям в скорости работы интерпретируемых программ.