Языки программирования и типизация#
Язык программирования – ЭТО:
Формальный язык для записи инструкций, выполняемых исполнителем.
ISO/IEC/IEEE 24765:2010 Systems and software engineering — Vocabulary
Язык предназначенный для представления программ
ГОСТ 28397-89 Языки программирования. Термины и определения
Язык программирования - это формальный инструмент, который позволяет разработчикам создавать программы для решения различных задач. Каждый язык имеет свой уникальный синтаксис и семантику, которые определяют, как писать и выполнять код. Языки программирования обеспечивают абстракцию, позволяющую программистам описывать операции, не вдаваясь в детали их реализации.
Языки программирования различаются по своей цели и области применения. Некоторые языки разработаны для системного программирования и максимальной производительности (например, C++), другие - для веб-разработки (например, JavaScript), а некоторые - для научных вычислений (например, Fortran).
Языки программирования имеют важное значение в современном мире информационных технологий и используются в различных областях, таких как разработка программного обеспечения, искусственный интеллект, анализ данных, веб-разработка, кибербезопасность и многие другие. Каждый язык имеет свои преимущества и ограничения, и выбор языка зависит от конкретной задачи и предпочтений разработчика.
Составляющие языка:
- Синтаксис - набор правил, определяющих последовательности символов, составляющих допустимую программу или ее фрагмент.
- Семантика - правила, определяющие значение различных конструкций языка.
- Система выполнения и стандартная библиотека - определяют функциональность, доступную для всех имплементаций языка без подключения внешних модулей.
Синтаксис и семантика языка#
Синтаксис и семантика - это два важных аспекта языка программирования, которые определяют, как писать и понимать программы.
Синтаксис - это правила, определяющие структуру и грамматику языка программирования. Он определяет, каким образом должен быть организован код, какие ключевые слова и символы используются, и какие правила должны соблюдаться при написании программ. Нарушение синтаксических правил приводит к ошибкам компиляции или выполнения. Примеры синтаксических элементов включают ключевые слова, операторы, скобки, знаки препинания и другие элементы кода.
Семантика - это правила, которые определяют, как код взаимодействует с данными и как выполняются операции. Семантика определяет, какие действия будут выполнены в результате выполнения кода, какие значения будут возвращены, и как код взаимодействует с окружающей средой. Ошибки семантики могут привести к неправильному поведению программы. Примеры семантических аспектов включают правила типизации, порядок выполнения операций и правила области видимости.
Синтаксические ошибки#
Синтаксические ошибки возникают из-за нарушения синтаксиса языка Python. Это означает, что код не соответствует правилам и структуре Python, что делает его некорректным с точки зрения интерпретатора Python.
Примеры:
- Отсутствие двоеточия в конце блока кода после оператора if, while, for, и т.д.
- Забытые или лишние скобки, кавычки или знаки препинания.
- Использование зарезервированных слов или имен переменных, которые не определены.
В C++, точка с запятой (;) используется для завершения выражений, фигурные скобки ({}) используются для определения блоков кода, и ключевые слова, такие как "if" и "for", обозначают условные операторы и циклы.
int x = 5; // Пример синтаксиса
if (x > 0) {
// Ещё один пример синтаксиса
// ...
}
Семантические ошибки#
Семантические ошибки проявляются в том случае, когда код, несмотря на корректный синтаксис, имеет неправильное значение или действие. Это означает, что интерпретатор не может выполнить код из-за его некорректного значения или использования.
•Примеры:
- Деление на ноль.
- Обращение к несуществующему элементу списка или словаря.
- Использование неверного типа данных в операции (например, попытка сложить строку и число).
В этом примере переменной x
присваивается значение 10, а переменной c
присваивается символ 'A'. Затем мы попытались выполнить операцию сложения между x
и c
, но это вызывает семантическую ошибку, так как попытка сложить целое число (int
) и символ (char
) не имеет смысла. В C++ символ 'A' имеет числовое представление (например, ASCII-код), но это не делает его совместимым с целыми числами. В результате компиляция этого кода вызовет ошибку.
int x = 10;
char c = 'A';
x = x + c; // Семантическая ошибка: нельзя сложить int и char
Поколения языков Generation Language — GL#
Языки программирования можно классифицировать на поколения в зависимости от их исторического развития, особенностей и уровня абстракции. Обычно выделяют пять основных поколения языков программирования:
1GL: Машинный код#
Первое поколение языков программирования, также известных как 1GL, включает в себя машинный код.
Машинный код - это наименьший уровень абстракции языков программирования. Он представляет собой набор двоичных инструкций, которые непосредственно выполняются процессором компьютера. Каждая инструкция машинного кода соответствует конкретной операции аппаратной части компьютера, такой как арифметические вычисления, загрузка и сохранение данных и управление потоком выполнения.
Первое поколение языков программирования предоставляет прямой доступ к аппаратной части компьютера. Программистам приходится точно знать аппаратные ресурсы и инструкции для их управления.
Код, написанный на языках первого поколения, часто зависит от конкретной архитектуры компьютера и не переносим между разными машинами без значительных изменений.
Первое поколение подвержено высокому уровню сложности и ошибок, так как программистам нужно было учитывать детали аппаратной части и манипулировать битами и байтами напрямую.
Первое поколение языков программирования было эффективным средством для написания программ в ранние дни компьютерной индустрии, но оно было трудоемким и требовало высокой квалификации. Впоследствии разработка высокоуровневых языков программирования с более высокой абстракцией значительно упростила процесс программирования и сделала его доступным для более широкой аудитории.
2GL: Ассемблер – язык низкого уровня#
Второе поколение языков программирования, также известных как 2GL, включает в себя языки ассемблера и низкоуровневые языки программирования, которые представляют собой промежуточный уровень абстракции между машинным кодом (первым поколением) и высокоуровневыми языками программирования (третьим поколением).
Ассемблер - это низкоуровневый язык программирования, который предоставляет символические имена и мнемоники для инструкций машинного кода и регистров компьютера. Ассемблер позволяет программистам управлять аппаратной частью компьютера, создавая программы, близкие к низкоуровневому уровню.
Ассемблер позволяет программистам управлять аппаратной частью компьютера, создавая программы, близкие к низкоуровневому уровню. В ассемблере используются символические имена, такие как ADD
для сложения, и мнемоники для инструкций, что делает код более читаемым. Программисты имеют доступ к регистрам компьютера, что позволяет управлять данными и потоком выполнения. Ассемблер предоставляет прямой доступ к аппаратуре и директивы для организации кода. Он является низкоуровневым языком и требует глубокого понимания аппаратной архитектуры, а код на ассемблере обычно зависит от конкретной архитектуры и не всегда переносим между разными компьютерами. Ассемблер используется в высокопроизводительных задачах и в разработке операционных систем, драйверов и встраиваемых системах.
Так как ассемблерный код однозначно переводится в машинный код для заданного процессора, это позволяет более полно использовать все возможности процессора, сокращать количество ненужных «холостых» операций и использовать прочие приёмы оптимизации программного кода, недоступные при использовании компиляторов, однако развитие оптимизирующих компиляторов приводит к тому, что качество генерируемого ими кода может быть выше, чем может написать программист на ассемблере средней квалификации. При этом чем больше объём программы, тем меньше выигрыш от использования языка ассемблера.
Программы на языке ассемблера практически невозможно перенести на машину с другой архитектурой или системой команд без переписывания программы, даже если при написании использовался «кроссплатформенный» диалект ассемблера: разные архитектуры процессоров имеют разные наборы регистров, флагов, разные размеры машинного слова, а также могут иметь узкоспециализированные команды, отсутствующие на других платформах.
Первые языки высокого уровня:#
Fortran (сокращение от "Formula Translation") - это старейший и один из самых важных языков программирования, который был разработан в 1950-х годах для научных и инженерных вычислений.
Структура программ изначально была ориентирована на ввод с перфокарт и имела ряд удобных именно для этого случая свойств. Так, с 1-й по 5-ю колонку располагалась область меток, 6-я служила для маркировки текста как продолжения предыдущей строки (любым символом, кроме пробела и «0»), а с 7-й по 72-ю располагался собственно текст оператора или комментария. Колонки с 73-й по 80-ю могли служить для нумерации карт (чтобы восстановить случайно рассыпавшуюся колоду) или для краткого комментария, транслятором они игнорировались. Если текст оператора не вписывался в отведённое пространство (с 7-й по 72-ю колонку), в 6-й колонке следующей строки ставился признак продолжения, и затем оператор продолжался на ней. Расположить два или более оператора в одной строке (карте) было нельзя. Когда перфокарты ушли в историю, эти достоинства превратились в серьёзные неудобства.
Именно поэтому к стандарту Фортрана, начиная с Fortran 90, с сохранением фиксированного формата исходного текста, был добавлен свободный формат, который не регламентирует позиции строки и позволяет записывать более одного оператора на строку. Введение свободного формата и современных способов структурного программирования позволило создавать код, читаемость и ясность которого не уступает коду, созданному при помощи других современных языков программирования, таких как Паскаль, C или Java. Современные среды разработки позволяют комбинировать форматы: например, расширять длину строки до свободного формата (обычно 132 символа), позволяют записывать несколько операторов в строке, но при этом позволяют также сохранять отступ слева (делать левое поле), характерный для старого фиксированного формата, оставляя тем самым выделенную колонку для меток ошибок и форматов, а также колонку продолжения строки.
Своего рода «визитной карточкой» старого Фортрана является огромное количество меток, которые использовались в операторах безусловного перехода GOTO, операторах циклов, в условных операторах и в операторах описания форматного ввода-вывода FORMAT. Большое количество меток и операторов GOTO часто делало программы на Фортране трудными для понимания.
Именно этот негативный опыт стал причиной, по которой в ряде современных языков программирования (например, в языке Java) метки и связанные с ними операторы безусловного перехода сильно видоизменены.
Однако современный Фортран (в основном начиная с версии Fortran’90) избавлен от избытка меток за счёт введения таких операторов, как DO … END DO, DO WHILE, SELECT CASE, конструкции IF THEN-ELSEIF THEN-ELSE-END IF и др. Более того, в современных стандартах языка оставлен лишь классический оператор GOTO, применяемый во многих языках и поныне. Вычисляемый оператор GOTO, а также конструкция ENTRY — множественного входа в процедуры, — были исключены из стандарта, хотя, как правило, продолжают поддерживаться компиляторами.
Основные достижения:
-
Переносимость на уровне исходных кодов
-
Использование абстракций высокого уровня:
-
Переменные, массивы
-
Операторы ветвлений, циклов, переходов
-
Функции, процедуры, подпрограммы
-
Работа с памятью
-
Библиотеки: работа с файлами, вводом/выводом и т.д.
-
Использование команд на естественном языке
Транслятор, компилятор, интерпретатор#
Первые трансляторы были разработаны для языков высокого уровня в середине 20-го века. Они служили для преобразования исходного кода, написанного на языке высокого уровня (например, Fortran или Cobol), в машинный код, который может выполняться компьютером. Это позволило программистам писать более абстрактный и человеко-читаемый код, который затем компилировался в машинный код для конкретной архитектуры.
Один из самых известных ранних трансляторов был компилятор Фортран (Fortran Compiler), созданный Джоном Бэкусом в 1957 году. Он был разработан для языка программирования Fortran и стал одним из первых успешных примеров трансляторов.
Трансляторы имели огромное значение, так как они сделали программирование более доступным и эффективным. Они позволили программистам сосредотачиваться на абстракциях и алгоритмах, а не на деталях аппаратной части компьютера. С течением времени трансляторы стали более совершенными и поддерживали различные языки программирования и архитектуры компьютеров.
1. Транслятор (Translator):
- Определение: Транслятор - это общий термин, который охватывает как компиляторы, так и интерпретаторы. Он представляет собой программу, которая переводит исходный код на языке программирования в машинный код или иную форму, которую компьютер может выполнить.
- Типы трансляторов: Включают компиляторы и интерпретаторы. Термин "транслятор" обобщает оба этих типа.
2. Компилятор (Compiler):
- Определение: Компилятор - это программа, которая преобразует исходный код целиком в машинный код или код на низкоуровневом языке, создавая таким образом исполняемый файл. Компилятор выполняет этот процесс один раз перед запуском программы.
- Преимущества: Компилированный код обычно работает быстрее, так как он предварительно оптимизирован и не требует перевода в машинный код во время выполнения программы. Также компилированный код обычно более эффективен с точки зрения производительности.
3. Интерпретатор (Interpreter):
- Определение: Интерпретатор - это программа, которая считывает исходный код построчно и выполняет его на лету, без создания отдельного исполняемого файла. Каждая строка кода интерпретируется и выполняется непосредственно во время выполнения программы.
- Преимущества: Интерпретированные языки программирования более гибкие, так как они позволяют изменять код и немедленно видеть результаты. Они также более переносимы, так как интерпретатор может быть реализован на разных платформах.
3GL: Процедурные языки#
3GL (Third Generation Language), или языки третьего поколения, представляют собой группу языков программирования, которые обладают более высоким уровнем абстракции по сравнению с ассемблером (2GL) и предназначены для разработки программ с использованием процедурного подхода.
Наиболее активный период разработки языков и систем программирования приходится на 1960-е годы.
За это десятилетие в мире родилось более тысячи разнообразных языков, как универсальных, так и специализированных, но выжили и доросли до XXI века дожили немногие, в том числе бессмертные Fotran, Basic, Algol, Cobol, Simula, Lisp и их потомки.
На рисунке: «вавилонская башня» языков программи-рования, созданных в 1960-е годы
Основные концепции::
- Процедурное программирование: 3GL, в частности процедурные языки, поддерживают методологию процедурного программирования, где программа разделяется на набор процедур или функций. Каждая процедура выполняет конкретную задачу и может вызываться из других частей программы.
- Абстракция данных: 3GL предоставляют возможность создавать пользовательские типы данных и структуры данных, что упрощает организацию данных и их обработку.
- Синтаксическая структура: 3GL имеют более высокий уровень абстракции с читаемым и понятным синтаксисом, что делает код более человеко-ориентированным.
- Портабельность: Код, написанный на 3GL, может быть перенесен между разными платформами с минимальными изменениями, так как языки 3GL обычно абстрагируются от деталей аппаратной части.
- Системные библиотеки: 3GL обычно поставляются с богатыми системными библиотеками, предоставляющими множество функций и ресурсов для разработки приложений.
Выполнение императивной программы#
Императивная программа использует именованные области памяти, такие как переменные, для хранения состояния вычислений и управления потоком выполнения. Это одна из ключевых особенностей императивного стиля программирования. Вот как это работает более подробно:
Инструкции программы, как и данные, хранятся в памяти компьютера. Однако они хранятся в разных сегментах памяти и имеют разное предназначение.
-
Инструкции программы, то есть машинные коды, хранятся в части памяти, которая называется сегментом кода (или текстовым сегментом). Этот сегмент памяти содержит байтовый код, который представляет собой инструкции, которые процессор должен выполнить. Кодовый сегмент является обычно доступным только для чтения и защищен от записи, чтобы предотвратить случайные или злонамеренные изменения инструкций программы.
-
Данные, такие как значения переменных, хранятся в сегменте данных. Этот сегмент памяти содержит как статические данные, которые объявлены в программе на этапе компиляции, так и динамические данные, которые выделяются и освобождаются во время выполнения программы. Переменные и данные могут быть изменены программой в соответствии с логикой программы.
Обращение к памяти и выполнение инструкций осуществляются процессором компьютера. Процессор считывает инструкции из кодового сегмента и выполняет их в соответствии с их содержанием. Время выполнения инструкций и доступ к данным в памяти управляется аппаратной архитектурой процессора и операционной системой.
Когда программа выполняется, процессор последовательно считывает инструкции из памяти, выполняет их, и при необходимости обращается к данным в памяти. Операционная система обеспечивает защиту памяти и управление доступом к ней, чтобы предотвратить незаконный доступ и обеспечить надежную работу программ.
Обращение к памяти и выполнение инструкций - это фундаментальные операции в работе компьютерных программ, и они представляют собой важные аспекты архитектуры компьютера и выполнения программ.
4GL: Объектно-ориентированные языки#
4GL (Fourth Generation Language) - это класс языков программирования, который был разработан для упрощения процесса программирования и увеличения продуктивности разработчиков.
Языки этого поколения предназначены для реализации крупных проектов, повышают их надежность и скорость создания, ориентированы на специализированные области применения, и используют не универсальные, а объектно-ориентированные языки, оперирующие конкретными понятиями узкой предметной области. В эти языки встраиваются мощные операторы, позволяющие одной строкой описать такую функциональность, для реализации которой на языках младших поколений потребовались бы тысячи строк исходного кода
Основные характеристики 4GL, включая объектно-ориентированные языки:
-
Уровень абстракции: 4GL языки работают на более высоком уровне абстракции, что позволяет программистам сосредотачиваться на решении бизнес-задач, а не на деталях управления памятью или железом.
-
Удобство использования: Они обычно имеют более простой и интуитивно понятный синтаксис, что делает их легче для изучения и использования.
-
Упор на бизнес-логику: 4GL-языки, в том числе объектно-ориентированные, спроектированы так, чтобы упростить разработку бизнес-приложений, баз данных и отчетов. Они часто предоставляют встроенную поддержку для работы с базами данных и графическим интерфейсом.
-
Объектно-ориентированный подход: В объектно-ориентированных 4GL-языках данные и функциональность объединяются в объекты, что позволяет создавать более модульные, переиспользуемые и понятные программы. ООП также поддерживает принципы инкапсуляции, наследования и полиморфизма.
-
Разработка на уровне приложения: 4GL-языки обычно ориентированы на разработку конкретных приложений и решение бизнес-проблем. Они могут включать интегрированные среды разработки (IDE) и инструменты для быстрой разработки приложений.
Smalltalk#
Smalltalk — объектно-ориентированный язык программирования с динамической типизацией, основанный на идее посылки сообщений, разработанный в Xerox PARC Аланом Кэем, Дэном Ингаллсом, Тедом Кэглером, Адель Голдберг, и другими в 1970-х годах. Представляет собой интегрированную среду разработки и исполнения, объекты которой доступны для модификации через неё саму, и программирование в которой в итоге сводится к модификации её собственного поведения. Язык был представлен как Smalltalk-80.
Smalltalk является одним из многих объектно-ориентированных языков, основанных на языке Симула, который сам оказал большое влияние на развитие таких объектно-ориентированных языков, как: Objective-C, Actor, Java, E.
Основными идеями Smalltalk являются:
- Всё — объекты, и всё их взаимодействие — через посылку сообщений. Строки, целые числа, логические значения, определения классов, блоки кода, стеки, память, составляющие самой интегрированной среды разработки и исполнения — всё представляется в виде объектов. У объектов есть методы и состояние. Любому объекту может быть послано любое сообщение. При отправке сообщения среда исполнения всегда ищет у объекта-получателя подходящий метод и выполняет его, а если не находит — выполняет у объекта-получателя специальный метод для неопознанных сообщений. Объект-получатель сам определяет, является ли полученное сообщение правильным, и что надо сделать, чтобы его обработать.
- Всё доступно для изменения. Если вы хотите изменить саму интегрированную среду разработки и исполнения, вы можете сделать это в работающей системе, без остановки, перекомпиляции и перезапуска. Если вам необходима в языке новая управляющая конструкция языка, вы можете добавить её. В некоторых реализациях вы можете также изменить синтаксис языка или способ работы сборщика мусора.
- Динамическая типизация — это означает, что вы не указываете типы переменных в программе, что делает язык гораздо лаконичней (как объяснено выше, является ли операция правильной, определяет объект-получатель, а не компилятор).
Влияние smaltalk#
Еще задолго до появления Smalltalk Алан Кей сформулировал три фундаментальных принципа объектно-ориентированного программирования:
- Объект - базовая единица объектно-ориентированной системы.
- Объекты могут обладать состоянием.
- Посылка сообщения - единственный способ обмена информацией между объектами.
Таким образом, видно, что изначальное определение существенно отличается от привычного нам инкапсуляция-полиморфизм-наследование, которое уже имело место до появления Smalltalk в языке Simula-67.
Smalltalk оказал огромное влияние на мир программирования и стал важным предшественником для многих современных языков и концепций программирования.
-
Объектно-ориентированное программирование (ООП): Smalltalk был одним из первых полностью объектно-ориентированных языков. Он внедрил ключевые концепции ООП, такие как классы, объекты, наследование, инкапсуляция и полиморфизм. Многие из этих концепций были заимствованы и усовершенствованы в языках, таких как C++, Java, C#, Ruby и Python.
-
Интерпретируемые языки: Smalltalk был одним из первых языков, который активно использовал интерпретацию кода. Это подход позволяет динамически изменять и создавать объекты и классы во время выполнения программы. Многие современные языки, такие как Python и Ruby, также используют интерпретацию.
-
Интегрированные среды разработки (IDE): Smalltalk предоставлял интегрированную среду разработки с мощными инструментами для создания, отладки и тестирования программ. Эта концепция IDE стала образцом для разработки сред разработки в других языках.
-
Графические пользовательские интерфейсы (GUI): Smalltalk был одним из первых языков, который активно использовался для создания графических пользовательских интерфейсов (GUI) и приложений с использованием оконных систем. Это вдохновило множество фреймворков и библиотек для создания GUI в других языках.
С – язык для профессионалов#
С — это компьютерный язык процедурного императивного программирования общего назначения. Он был разработан в 1972 году Деннисом М. Ритчи в исследовательском центре Bell Telephone Laboratories для создания операционной системы UNIX. В 1978 году Брайан Керниган и Деннис Ритчи подготовили первое общедоступное описание языка C, известное сегодня как стандарт K&R.
Операционная система UNIX, компилятор C и практически все прикладные программы UNIX были написаны на языке С. Он является наиболее широко практикуемым компьютерным языком. По частоте использования C разделяет первое место с Java, который также не менее популярен и очень распространен среди современных программистов.
Существует несколько легенд, касающихся причин разработки Си и его отношения к операционной системе UNIX, включая следующие:
По одной легенде разработка Си стала результатом того, что его будущие авторы любили компьютерную игру, подобную популярной игре Asteroids (Астероиды). Они уже давно играли в неё на главном сервере компании, который был недостаточно мощным и должен был обслуживать около ста пользователей. Томпсон и Ритчи посчитали, что им не хватает контроля над космическим кораблём для того, чтобы избегать столкновений с некоторыми камнями. Поэтому они решили перенести игру на свободный PDP-7, стоящий в офисе. Однако этот компьютер не имел операционной системы, что заставило их её написать. В конце концов, они решили перенести эту операционную систему ещё и на офисный PDP-11, что было очень тяжело, потому что её код был целиком написан на ассемблере. Было вынесено предложение использовать какой-нибудь высокоуровневый портируемый язык, чтобы можно было легко переносить ОС с одного компьютера на другой. Язык Би, который они хотели сначала задействовать для этого, оказался лишён функциональности, способной использовать новые возможности PDP-11. Поэтому они и остановились на разработке языка Си.
Существует и другая легенда. Первый компьютер, для которого была первоначально написана UNIX, предназначался для создания системы автоматического заполнения документов. Первая версия UNIX была написана на ассемблере. Позднее для того, чтобы переписать эту операционную систему, был разработан язык Си.
Структура и компоненты простой программы на С/C++#
Процесс создания исполняемой программы на языке C. Этот процесс включает несколько этапов, начиная с исходного текста и заканчивая исполняемым файлом. Вот более подробное описание каждого этапа:
-
Исходный текст: Начальным этапом является написание исходного текста программы на языке C. Этот текст содержит инструкции и объявления, определяющие логику и структуру программы.
-
Препроцессинг: Исходный текст программы подвергается препроцессингу, где выполняются различные манипуляции с исходным кодом. Это включает в себя включение библиотечных файлов (
#include
), макроподстановки (#define
), условную компиляцию и другие директивы препроцессора. Результат препроцессинга - это расширенный исходный текст. -
Компиляция: Расширенный исходный текст передается компилятору C (например, GCC для Unix-подобных систем). Компилятор анализирует исходный текст и создает объектный файл (обычно с расширением
.o
или.obj
). Этот файл содержит машинный код, который соответствует исходному тексту программы, но еще не зависит от конкретной архитектуры. -
Компоновка: На этом этапе объектные файлы, а также библиотеки, необходимые для программы, объединяются компоновщиком (линкером). Компоновка включает в себя разрешение всех ссылок на функции и переменные между объектными файлами и библиотеками, а также создание исполняемого файла.
-
Исполняемый файл: На этом этапе компоновщик создает исполняемый файл, который может быть запущен на конкретной архитектуре и операционной системе. Исполняемый файл содержит бинарный код программы и дополнительную информацию о структуре программы.
После завершения всех этих этапов, вы получаете готовую исполняемую программу, которую можно запустить на целевой системе. Этот процесс обеспечивает переносимость программ между разными платформами, так как исходный код компилируется в объектный код, который не зависит от конкретной архитектуры.
Java – дитя интернета#
В 1995 г. фирма Sun Microsystems представила язык Java для программирования в интернете.
Он возник в ходе реализации проекта Oak («Дуб»), целью которого было создание системы программирования бытовых микропроцессорных устройств.
В конце 20-го и начале 21-го века бурное развитие интернет-технологий вызвало необходимость в создании универсальных и безопасных средств для разработки приложений, способных работать в различных средах и на разных платформах. В этом контексте, появление Java и её виртуальной машины (JVM) оказалось переломным моментом в мире программирования.
Java была разработана с учетом потребностей развивающегося интернета, где сетевые и веб-приложения стали играть все более важную роль. Её ключевые черты и особенности стали идеально сочетаться с требованиями интернет-технологий:
- Портируемость: Java предоставляет средство для написания кода один раз и его последующего выполнения на разных операционных системах и архитектурах благодаря JVM, которая компилирует байт-код в машинный код конкретной платформы.
- Сетевая ориентированность: Java изначально была нацелена на создание сетевых приложений, и её библиотеки содержат множество инструментов для работы с сетью, вводом/выводом и протоколами.
- Безопасность: JVM обеспечивает изоляцию и контроль доступа, что стало важным аспектом в безопасности при выполнении кода, полученного из интернета.
- Многозадачность: Java обеспечивает поддержку многозадачности, что позволяет эффективно обрабатывать параллельные запросы и события, что характерно для интернет-приложений.
Java стала не только популярным языком для разработки серверных приложений и веб-сервисов, но и сыграла важную роль в мире мобильных приложений через платформу Android. Её популярность и обширная экосистема библиотек и фреймворков стали движущей силой за развитием множества веб-приложений, серверных систем и мобильных приложений, которые в наши дни составляют значительную часть интернет-технологий и онлайн-сервисов.
Основные особенности языка
Программы на Java транслируются в байт-код Java, выполняемый виртуальной машиной Java (JVM) — программой, обрабатывающей байтовый код и передающей инструкции оборудованию как интерпретатор.
Достоинством подобного способа выполнения программ является полная независимость байт-кода от операционной системы и оборудования, что позволяет выполнять Java-приложения на любом устройстве, для которого существует соответствующая виртуальная машина. Другой важной особенностью технологии Java является гибкая система безопасности, в рамках которой исполнение программы полностью контролируется виртуальной машиной. Любые операции, которые превышают установленные полномочия программы (например, попытка несанкционированного доступа к данным или соединения с другим компьютером), вызывают немедленное прерывание.
Часто к недостаткам концепции виртуальной машины относят снижение производительности. Ряд усовершенствований несколько увеличил скорость выполнения программ на Java:
- применение технологии трансляции байт-кода в машинный код непосредственно во время работы программы (JIT-технология) с возможностью сохранения версий класса в машинном коде,
- обширное использование платформенно-ориентированного кода (native-код) в стандартных библиотеках,
- аппаратные средства, обеспечивающие ускоренную обработку байт-кода (например, технология Jazelle, поддерживаемая некоторыми процессорами архитектуры ARM).rlang, Groovy, Ruby и многих других.
5GL: Современность#
Концептуальные отличия ЭВМ V поколения:
- новая технология производства микросхем, знаменующая переход от кремния к арсениду галлия, и дающая возможность на порядок повысить быстродействие основных логических элементов;
- новая архитектура (не фон-неймановская);
- новые способы ввода-вывода информации — распознавание и синтез речи и образов;
- отказ от традиционных алгоритмических языков программирования (Фортран, Алгол и т. п.) в пользу декларативных;
- ориентация на задачи искусственного интеллекта с автоматическим поиском решения на основе логического вывода.
Prolog#
Prolog (Programming in Logic) - это декларативный язык программирования, который основан на логическом программировании. Важной чертой Prolog является то, что программы на нем описывают отношения и факты, а не последовательность шагов для выполнения конкретной задачи. Основной идеей логического программирования в Prolog является использование формальной логики для решения задач.
Основные характеристики и особенности языка программирования Prolog:
-
Логическое программирование: Prolog основан на предикатах и правилах, которые определяют логические отношения между объектами. Программы на Prolog описывают факты и правила, а система Prolog использует эти описания для вывода результатов на основе заданных запросов.
-
Факты и правила: Факты - это утверждения о мире, которые истинны. Правила - это логические высказывания, определяющие отношения на основе фактов. Программы на Prolog состоят из фактов и правил.
-
Инверсия управления: В языках программирования, таких как C++ или Java, разработчик описывает последовательность шагов для решения задачи. В Prolog разработчик описывает отношения и правила, и система Prolog автоматически находит решение на основе этих описаний.
-
Рекурсия: Prolog поддерживает рекурсию как способ описания повторяющихся операций и поиска решений.
-
Применения: Prolog широко используется в искусственном интеллекте, обработке естественного языка, системах экспертных знаний, робототехнике и других областях, где требуется логическое выводное и решение сложных логических задач.
-
Процедура запроса: В Prolog пользователь задает запрос, и система Prolog пытается найти решение, используя описанные факты и правила. Если решение найдено, оно возвращается в качестве ответа.
Выполнение декларативной программы#
Декларативные программы, как правило, не взаимодействуют напрямую с памятью, а описывают желаемые результаты или свойства данных, а затем поручают интерпретатору или среде выполнения заботу о выполнении этих описаний. Это одна из ключевых особенностей декларативного стиля программирования.
В декларативной программе программист описывает "что" должно быть сделано или "что" должно быть достигнуто, но не указывает "как" это должно быть сделано. Интерпретатор или среда выполнения берет на себя ответственность за выполнение инструкций программы, управление данными и ресурсами, а также оптимизацию выполнения.
Python#
Python - это высокоуровневый, интерпретируемый язык программирования, созданный Гвидо ван Россумом в начале 1990-х годов. Он известен своим простым и читаемым синтаксисом, что делает его идеальным выбором для начинающих программистов. Python также популярен среди опытных разработчиков благодаря множеству библиотек и фреймворков для разнообразных задач.
Python поддерживает динамическую типизацию, что означает, что тип переменной определяется автоматически на основе значения, которое ей присвоено. Это делает язык гибким и удобным для работы с данными разного типа.
Создавая Python, Гвидо ван Россум стремился сделать язык, который был бы простым и интуитивным для программистов, и он достиг этой цели. Язык Python стал одним из самых популярных в мире, и он нашел применение во многих областях, включая веб-разработку, научные исследования, анализ данных, искусственный интеллект, разработку игр и многое другое.
Благодаря активному сообществу разработчиков и богатой стандартной библиотеке Python, создание приложений стало более доступным и эффективным. Python продолжает расти в популярности и остается одним из наиболее важных и востребованных языков программирования в современном мире.
Текст философии Python:
- Читаемость кода имеет значение (Readability counts):** Python ставит акцент на читаемости кода. Он призывает разработчиков писать код так, чтобы его было легко читать и понимать. Это делает сотрудничество над проектами более эффективным и способствует поддержке и сопровождению кода в долгосрочной перспективе.
- Явное лучше, чем неявное (Explicit is better than implicit): Python поощряет явное описание действий и отношений в коде. Это означает, что лучше явно указывать, что делает код, чем скрывать действия в неявных механизмах.
- Простота и ясность (Simple is better than complex): Python предпочитает простой и ясный код сложному и запутанному. Это способствует тому, чтобы код был понятным и поддерживаемым.
- Сложность должна скрываться (Complex is better than complicated): Если сложность необходима, то лучше делать ее очевидной, но не усложнять код излишне. Простой и ясный интерфейс скрывает сложность реализации.
- Плоский лучше, чем вложенный (Flat is better than nested): Python поощряет минимизацию вложенности в коде. Это делает код более понятным и уменьшает сложность его понимания.
- Один правильный способ сделать это (There should be one-- and preferably only one --obvious way to do it): Python призывает разработчиков использовать единственный, очевидный способ решения задачи, чтобы избегать излишней сложности и разнородности в коде.
- Сейчас лучше, чем никогда (Now is better than never): Python призывает к действию и решению проблем здесь и сейчас, вместо откладывания их на потом.
- Хотя никогда не бывает плохо, иногда лучше никогда (Although never is often better than right now): Python признает, что иногда лучше подождать и внимательно продумать решение, чем торопиться и делать что-то непродуманное.
- Если реализацию сложно объяснить - это плохая идея (If the implementation is hard to explain, it's a bad idea): Python призывает к созданию кода, который легко объясним и понятен другим разработчикам.
- Пространство имен - отличная штука, давайте делать их больше (Namespaces are one honking great idea -- let's do more of those!): Python поощряет использование пространств имен (namespaces) для организации кода и избегания конфликтов имён.
Влияние других языков на Python
Появившись сравнительно поздно, Python создавался под влиянием множества языков программирования:
- ABC (англ.) — отступы для группировки операторов, высокоуровневые структуры данных (map) (фактически, Python создавался как попытка исправить ошибки, допущенные при проектировании ABC);
- Modula-3 — пакеты, модули, использование else совместно с try и except, именованные аргументы функций (на это также повлиял Common Lisp);
- Си, C++ — некоторые синтаксические конструкции (как пишет сам Гвидо ван Россум — он использовал наиболее непротиворечивые конструкции из С, чтобы не вызвать неприязнь у Си-программистов к Python);
- Smalltalk — объектно-ориентированное программирование;
- Lisp — отдельные черты функционального программирования (lambda, map, reduce, filter и другие);
- Fortran — срезы массивов, комплексная арифметика;
Miranda — списочные выражения;
- Java — модули logging, unittest, threading (часть возможностей оригинального модуля не реализована), xml.sax стандартной библиотеки, совместное использование finally и except при обработке исключений, использование @ для декораторов;
Большая часть других возможностей Python (например, байт-компиляция исходного кода) также была реализована ранее в других языках.
Типизация#
Типизация - это концепция в программировании, которая определяет, какие типы данных могут быть присвоены переменным и какие операции можно выполнять над этими данными. Типизация играет важную роль в обеспечении правильности и безопасности программ.
Система типов#
Система типов языка программирования - совокупность правил, определяющих свойство типа для конструкций языка (переменных, выражений, функций, модулей, …).
Цели системы типов:
▶ основная: определение интерфейсов для взаимодействия с частями программы и обеспечение их корректного использования с целью устранения ошибок;
▶ обеспечение функциональности языка (напр., динамическая диспетчеризация в ООП);
▶ рефлексия;
▶ оптимизация;
▶ повышение доступности программы для понимания.
Статическая и динамическая типизация#
Cтатическая и динамическая типизация - это два основных подхода к системам типов в языках программирования. Они определяют, когда и как происходит проверка и установка типов данных в коде. Вот более подробное описание каждой из них:
Статическая типизация:#
-
Определение типов на этапе компиляции: В статически типизированных языках, типы данных переменных определяются и проверяются на этапе компиляции, то есть перед выполнением программы. Компилятор анализирует код и обеспечивает соответствие типов данных во всем коде.
-
Объявление типов переменных: Вам необходимо явно объявлять типы переменных при их создании или объявлении. Например, в языке C++:
int x = 10; // переменная x имеет тип int (целое число)
-
Статическая проверка типов: Статическая типизация обеспечивает статическую проверку типов, что означает, что компилятор проверяет, что операции и присваивания типов совместимы во всех местах программы. Если обнаружится несоответствие типов, компиляция завершится с ошибкой.
-
Преимущества: Статическая типизация обычно обеспечивает более высокую безопасность, раннее обнаружение ошибок и улучшенную производительность из-за оптимизаций, которые компилятор может выполнить на этапе компиляции.
-
Недостатки: Некоторые разработчики считают, что статическая типизация требует больше написания кода из-за необходимости объявления типов переменных, и она менее гибкая в отношении динамических изменений типов данных.
Динамическая типизация:#
-
Определение типов на этапе выполнения: В динамически типизированных языках, типы данных переменных определяются и проверяются во время выполнения программы. Переменные могут изменять свой тип в процессе выполнения.
-
Неявное объявление типов: Вам не нужно явно объявлять типы переменных; типы определяются автоматически на основе значений, которые вы присваиваете переменным. Например, в Python:
x = 10 # переменная x может быть целым числом x = "hello" # затем она может стать строкой`
-
Динамическая проверка типов: В динамически типизированных языках проверка типов выполняется во время выполнения программы, и ошибки связанные с типами, могут возникнуть только при выполнении кода.
-
Преимущества: Динамическая типизация обеспечивает более высокую гибкость, позволяя переменным изменять типы, и может сократить количество кода за счет неявного объявления типов.
-
Недостатки: Один из недостатков динамической типизации - это более сложное обнаружение ошибок на этапе разработки и меньшая безопасность в связи с возможностью неожиданных изменений типов данных во время выполнения.
Выбор между статической и динамической типизацией зависит от конкретных потребностей проекта и предпочтений разработчиков. Каждый подход имеет свои сильные и слабые стороны, и правильный выбор зависит от конкретных задач и контекста разработки.
Утиная типизация#
Утиная типизация (Duck Typing) - это концепция в программировании, которая фокусируется на поведении объектов (их методах и свойствах) вместо их явном типе. Термин "утиная типизация" происходит от выражения "Если она выглядит как утка, плавает как утка и крякает как утка, то она, вероятно, и есть утка." Это означает, что в утиной типизации объекты определяются на основе их функциональных характеристик, а не на основе их явного типа.
Основные идеи утиной типизации:
-
Проверка на основе поведения: Вместо того, чтобы опираться на явное объявление типов или интерфейсов, утиная типизация проверяет объекты на наличие конкретных методов или свойств во время выполнения программы. Если объект обладает необходимым поведением, он считается допустимым для использования в данном контексте.
-
Принцип подмены (Liskov Substitution Principle): Концепция утиной типизации следует принципу подмены, который утверждает, что объекты подтипа должны быть способны заменить объекты супертипа без изменения корректности программы.
-
Гибкость и расширяемость: Утиная типизация позволяет создавать более гибкий и расширяемый код, так как разработчики могут сосредотачиваться на функциональности объектов, а не на формальных декларациях типов.
Сильная и слабая типизация#
Типобезопасность (англ. type safety) предотвращение языком программирования ошибок согласования типов (напр., интерпретации целого числа int как вещественного float).
Безопасность памяти (англ. memory safety) предотвращение ЯП доступа к оперативной памяти, не выделенной в ходе выполнения программы.
Сильная и слабая типизация - это два свойства системы типов, которые определяют, насколько строго выполняются правила преобразования и совместимости типов данных в языках программирования. Давайте разберемся, в чем заключается разница между ними:
Сильная типизация :#
-
Строгие правила преобразования: В языках с сильной типизацией выполнение операций с различными типами данных строго контролируется, и преобразования между типами выполняются только в явных и безопасных случаях.
-
Безопасность и надежность: Сильная типизация способствует безопасности и надежности кода, так как она предотвращает неявные и потенциально опасные преобразования типов. Ошибки, связанные с типами, часто обнаруживаются на этапе компиляции.
-
Пример: В языке C++ с сильной типизацией не допустимо выполнять операцию сложения между целым числом и строкой без явного преобразования типов.
int x = 10; std::string text = "20"; int result = x + text;
// Ошибка компиляции из-за несоответствия типов`
Слабая типизация:#
-
Гибкие правила преобразования: В языках с слабой типизацией операции между различными типами данных выполняются без строгой проверки типов. Преобразования между типами данных могут происходить неявно и автоматически.
-
Гибкость и удобство: Слабая типизация может сделать код более гибким и менее явным, так как разработчику не нужно беспокоиться о явных преобразованиях типов.
-
Пример: В языке JavaScript с слабой типизацией операции между числами и строками могут выполняться без явного преобразования.
var x = 10; var text = "20";
var result = x + text; // Результат: "1020"`
Выбор между сильной и слабой типизацией зависит от конкретных требований проекта и стиля программирования. Сильная типизация обычно способствует безопасности и надежности кода, особенно в крупных и сложных проектах, но может требовать больше кода для явных преобразований типов. Слабая типизация может сделать код более кратким и гибким, но может привести к неожиданным результатам и ошибкам, если не учитывать правила преобразования.
Явная и неявная типизация#
Явная (явная типизация) и неявная (неявная типизация) типизация относятся к тому, как языки программирования обрабатывают объявление и использование типов данных. Давайте разберемся, в чем заключается разница между ними:
Явная типизация (Explicit Typing):#
-
Объявление типов явно: При использовании явной типизации программист должен явно указывать типы данных при объявлении переменных или при выполнении операций, которые требуют явного указания типов.
-
Преимущества: Явная типизация может улучшить читаемость кода и предоставить дополнительную информацию о типах переменных, что может помочь в обнаружении ошибок и улучшении поддержки инструментов разработки.
-
Пример на языке C++:
int x = 10; // явное объявление типа int
double y = 5.0; // явное объявление типа double`
Неявная типизация (Implicit Typing):#
-
Типы выводятся автоматически: При использовании неявной типизации язык программирования автоматически определяет типы переменных на основе значений, которые им присваиваются. Программисту не нужно явно указывать типы.
-
Преимущества: Неявная типизация может сделать код более кратким и гибким, так как программисту не нужно постоянно указывать типы. Это также способствует увеличению производительности кода, так как компилятор или интерпретатор может выполнять оптимизации на этапе компиляции или выполнения.
-
Пример на языке Python:
x = 10 # тип int определяется автоматически
y = 5.0 # тип float определяется автоматически`
Выбор между явной и неявной типизацией зависит от предпочтений программистов и требований конкретного проекта. Явная типизация может обеспечить более явное и безопасное управление типами данных, но требует больше написания кода. Неявная типизация может упростить написание кода и сделать его более гибким, но может привести к неожиданным результатам, если типы данных не учитываются внимательно.
Реализация ЯП#
Компилируемые языки программирования:#
-
Принцип работы: В компилируемых языках программирования исходный код программы преобразуется в машинный код или низкоуровневый бинарный код на этапе компиляции. Этот машинный код специфичен для целевой архитектуры и операционной системы.
-
Преимущества: Компиляция обычно обеспечивает более высокую производительность, так как программа выполняется на близком к аппаратному уровню языке. Также компилированный код может быть скомпилирован заранее и запущен на целевой платформе без необходимости наличия исходного кода.
-
Примеры: C, C++, Pascal, Ada.
Интерпретируемые языки программирования:#
-
Принцип работы: В интерпретируемых языках исходный код программы выполняется построчно или по блокам интерпретатором (или виртуальной машиной). Код преобразуется в машинные инструкции во время выполнения.
-
Преимущества: Интерпретация обеспечивает более гибкий и многоплатформенный подход, так как исходный код не зависит от архитектуры и операционной системы. Он может быть изменен и выполнен на разных платформах без перекомпиляции.
-
Примеры: Python, JavaScript, PHP, Ruby.
Компиляция в независимый от платформы код и его интерпретация:#
-
Принцип работы: Этот подход объединяет элементы компиляции и интерпретации. Исходный код компилируется в промежуточный код (например, байт-код или CIL - Common Intermediate Language), который не привязан к конкретной архитектуре. Затем этот промежуточный код интерпретируется или компилируется в машинный код виртуальной машиной или средой выполнения.
-
Преимущества: Этот метод позволяет достичь многоплатформенности, а также предоставляет некоторые преимущества в производительности и безопасности благодаря промежуточному коду.
-
Примеры: Java (байт-код), C# (CLR - Common Language Runtime).