Файл: Программирование и языки программирования: определения, инструменты и характерные особенности.pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 14.03.2024

Просмотров: 33

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Языки четвертого и пятого поколения направлены в основном на выражение того, что именно должна делать программа, это основа декларативного программирования. На таких языках чаще всего достаточно просто перекомпилировать программу под ту или иную компьютерную архитектурную и операционную систему. Помимо этого, для них не явялется редкостью компиляция в язык третьего поколения (чаще всего Си, но иногда это может быть Java, JavaScript, Ada и др.).

Языки высокого уровня зачастую порождают машинный код куда большего размера и выполняются несколько медленнее. Однако, для алгоритмически и структурно сложных программ, те или иные языки высокого уровня могут быть заметно эффективнее, уступая низкоуровневым только на маленьких и несложных программах. Проще говоря, потенциальная эффективность языка может изменяться с повышением его «уровня» нелинейно, а порой в принципе неоднозначно. Скорость разработки и трудоемкость модификации, стабильность и прочие показатели качества в сложных системах зачастую куда важнее предельно возможной скорости исполнения – они гарантируют наличие различий между рабочей и нерабочей программой, так что экономически более целесообразной является эволюция аппаратного обеспечения и методов оптимизирующей компиляции [6, c.56].

Именно потому в наши дни языки низкого уровня применяются только в задачах системного программирования. В результате этого, сам термин «низкоуровневый язык программирования» большинством людей понимается только лишь как «предоставляющий возможность решать задачи системного программирования». В реальности, это определение подходит и многим языкам, требующим существенных преобразований до получения машинного кода. Например, языки C# и Limbo применяется именно в роли системных в операционных системах с высокой отказоустойчивостью. Язык BitC является представителем четвертого поколения (функциональной парадигмы программирования), тем не менее он полностью ориентирован конкретно на системное программирование. Таким образом, классификация его как языка «низкого» или «высокого» уровня становится весьма субъективной.

Безопасные и небезопасные языки

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


Системы типов языков могут быть разделены на динамические (потомки Lisp, Smalltalk, APL) и статические, которые также делятся на неполиморфные (потомки Алгола и BCPL) и полиморфные (потомки ML). Помимо этого, они делятся на явные (explicit) и неявные (implicit) – иными словами, такие, которые требуют манифестной декларации типов или статически выводящие их самостоятельно [4, c.93].

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

В целом, язык можно назвать безопасным, если программы, написанные на нем, могут быть приняты компилятором как построенные правильно, в динамике эти программы никогда не выйдут за рамки ожидаемого поведения. Это не означает, что такого рода программы не содержат в себе ошибок в принципе. Термин «хорошее поведение программы» (good behavior) дает понять, что даже том случае, когда программа имеет логическую ошибку, то она все же не может нарушить целостность данных и обрушиться (crash).

Хотя приведенные термины неформальны, безопасность некоторых языков (например, Standard ML) может быть обоснована математически. Безопасность других языков (например, Ada) была гарантировано ad hoc-образом, без гарантии концептуальной целостности, что может обернуться катастрофами, если в ответственных задачах положиться на них. Это раскрывает принцип концептуальной целостности языков.

Например, Standard ML не допускает над данными проведения операций, не разрешенных явно и формализованных; тем не менее, программы на нем все же могут заканчиваться порождением необработанного исключения (деление на 0). Потомок Standard ML – MLPolyR – гарантирует отсутствие необработанных исключений. Такие языки носят название «типобезопасными». Например, Java и C# менее строги и осуществляют лишь контроль утечки памяти, поэтому в контексте этих языков чаще применяют более специфический термин «безопасность типов в отношении доступа к памяти» (memory type safety) [1, c.83].

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


Языки Си и C++ являются небезопасными. В программах, которые написаны на них, часто встречаются ситуации снижения типизации (приведение типов) и ее нарушения (каламбур типизации), таким образом, ошибки доступа к памяти в них представляют собой статистическую норму. Самые мощные системы статического анализа для этих языков могут обнаружить всего 70-80 % ошибок (PVS-Studio), их использование стоит очень дорого. У Си есть и безопасные потомки, типа Cyclone или Rust.

Язык Forth не претендует на звание «безопасного», но, тем не менее, на практике существование программ, способных повредить данные, почти исключено, так как содержащая потенциально опасную ошибку программа аварийно завершается на первом же тестовом запуске, принуждая к коррекции исходного кода. В сообществе Erlang принят подход «let it crash» (с англ. – «дай ей обрушиться»), также нацеленный на раннее выявление ошибок.

Компилируемые и интерпретируемые языки

Выделим три основных и принципиально разных способа реализации языков программирования, такие как: компиляция, интерпретация и встраивание. Считается, что способ реализации является присущим данному конкретному языку свойством, однако это заблуждение. В реальности, это деление до некоторой степени является условным. Чаще всего язык обладает формальной семантикой, которая ориентирована на интерпретацию, однако все или же почти все его реальные реализации представляют собой компиляторы, иногда довольно эффективно оптимизирующими (Standard ML, Haskell). Существуют языки, в которых граница между интерпретацией и компиляцией весьма размыта – например, Forth [10, c.73].

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

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


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

Для любого традиционно компилируемого языка можно реализовать интерпретатор. Однако многие интерпретируемые языки дают определенные дополнительные возможности, в виде динамической генерации, и их компиляция должна быть динамической. Так, термин «язык + реализация» в ряде случаев вполне уместен. Помимо этого, большинство современных «чистых» интерпретаторов не выполнятют конструкции языка непосредственно, а осуществляют компиляцию их в некоторое высокоуровневое промежуточное представление. Большинство традиционно интерпретируемых или компилируемых языков могут реализовываться как встраиваемые, хотя метаязыков, которые были бы способны охватить другие языки как свое подмножество, не так много (например, Lisp) [12, c.65].

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

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

Некоторые языки, такие как, Java и C#, находятся где-то между компилируемыми и интерпретируемыми. Программа, написанная на этих языках, компилируется не в машинный язык, а в машинно-независимый код низкого уровня, байт-код. Байт-код же выполняется виртуальной машиной.


Для выполнения байт-кода чаще всего используется интерпретация, хотя отдельные его части для ускорения работы программы могут быть транслированы в машинный код непосредственно во время выполнения программы по технологии компиляции «на лету» (Just-in-time compilation, JIT). Для Java байт-код исполняется виртуальной машиной Java (Java Virtual Machine, JVM), для C# – Common Language Runtime.

Подобный подход в некотором смысле позволяет использовать плюсы как интерпретаторов, так и компиляторов.

2. Критерии выбора языка программирования

2.1. Описание и характеристика критериев выбора языка программирования

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

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

Рассматриваемый нами перечень критериев определяет основу сравнения языков программирования по предоставляемым ими возможностям.

История и философия

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

Синтаксис