§ 2. Языки программирования

2.1. Высокоуровневые языки программирования

Любой алгоритм рассчитан на кон­кретного исполнителя, имеющего свою систему команд. Алгоритм для компьютера должен быть записан с помощью команд, которые компьютер может выполнять.

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

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

Языки программирования высоко­го уровня, или высокоуровневые язы­ки программирования (пример 2.1), были разработаны для того, чтобы суть алгоритма могла не зависеть от аппа­ратной реализации компьютера. Для преобразования текста программы, написанной на языке высокого уров­ня, в элементарные машинные коман­ды используются специальные про­граммы — трансляторы  (пример 2.2). Например, в тексте программы доста­точно написать «while», а уже транс­лятор языка переведет эту команду в последовательность машинных кодов. Принципы работы трансляторов за­висят от аппаратного и программного обеспечения компьютера.

Языки программирования высоко­го уровня являются формальными

искусственными языками. У каждого языка программирования можно вы­делить две составляющие: синтаксис и семантику. Синтаксис (грамматика языка) — совокупность правил для написания программы. Семантика — смысловая сторона языка (определяет смысловое содержание языковой кон­струкции).

Текст программы на языке высоко­го уровня представляет собой обыч­ный текстовый файл. Для его «чте­ния» и превращения в последователь­ность машинных команд выполняется анализ текста программы — проверка на соответствие синтаксическим пра­вилам и семантике данного языка программирования. В случае обна­ружения несоответствия компилятор может выдавать сообщения об ошиб­ках (пример 2.3).

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

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

2.2.  Парадигмы программирования

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

Рассмотрим некоторые парадигмы программирования.

Структурное программирование — парадигма программирования, в осно­ве которой лежит представление про­граммы в виде блоков иерархиче­ской структуры. Разработана в конце 1960-х — начале 1970-х гг. В соответ­ствии с данной парадигмой любая про­грамма состоит из трех базовых управ­ляющих структур: ветвление, цикл и последовательность; кроме того, ис­пользуются подпрограммы (пример 2.4). Разработка программы ведется по­шагово, методом «сверху вниз» (нис­ходящее программирование): сначала определяются цели решения задачи, а затем идет детализация каждого шага, который, став отдельной задачей, так­же может детализироваться.

Процедурное программирование — парадигма программирования, при которой последовательно выполняе

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

Парадигмы структурного и проце­дурного программирования основаны на подпрограммах. Разница заключа­ется в порядке их разработки: «сверху вниз» или «снизу вверх».

Функциональное программиро­вание — парадигма программирова­ния, в которой процесс вычисления трактуется как вычисление значений функций в математическом понима­нии. Основывается функциональное программирование на вычислении ре­зультатов функций от исходных дан­ных и результатов других функций и не предполагает явного хранения со­стояния программы (пример 2.6).

Объектно-ориентированное про­граммирование (ООП) — парадигма программирования, основанная на представлении программы в виде со­вокупности объектов и отражении их взаимодействия. Концепция ООП по­зволяет объединить данные и алгорит­мы их обработки в единую структуру, называемую классом. Каждый объект является экземпляром какого-либо класса (пример 2.7). В ООП програм­ма представляет собой набор объек­тов, имеющих состояние и поведение. Состояние и поведение объекта мо­жет измениться в результате события.

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

Большинство современных языков программирования являются мультипарадигменными — поддерживают сразу несколько парадигм програм­мирования (пример 2.8).

Отдельно рассматривают такие клас­сы языков программирования, как учебные (пример 2.9) и эзотерические языки программирования (пример 2.10).

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

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

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

  • буквы — {AaBbCcDd…};
  • цифры — {0 1 2 3 4 5 6 7 8 9};
  • знаки арифметических операций {× / + – …};
  • знаки сравнения — {< > = …};
  • разделители — {. , ; : ( ) { } [ ]… };
  • служебные слова — {if    while
  • for и т. д.};
  • комментарии — любой набор сим­волов и др.

Несмотря на значительные различия между языками, многие фундаменталь­ные понятия в большинстве языков программирования схожи между собой (пример 2.12). Согласно известной фор­муле Н. Вирта «Алгоритмы + структу­ры данных = программы», рассматривая язык программирования, нужно гово­рить о способах записи команд алго­ритма с помощью операторов и органи­зации работы с данными (пример 2.13).

Операторы

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

Выделяют следующие операто­ры: оператор присваивания, оператор условного перехода (ветвления), опе­ратор цикла (пример 2.14), оператор выбора, составной оператор, иногда используют пустой оператор, оператор безусловного перехода и др.

Все операторы языка в тексте про­граммы отделяются друг от друга с по­мощью явных или неявных разделите­лей (в Pascal таким разделителем яв­ляется «;»). Операторы выполняются в том порядке, в котором они записаны в тексте программы. Порядок выпол­нения операторов может быть изме­нен только посредством управляющих операторов: ветвления, цикла и др.

Данные

Большая часть операторов предна­значена для обработки величин. Ве­личина характеризуется типом, име­нем и значением. Типы данных могут быть простыми и структурированными (пример 2.15). Величина простого типа в каждый момент имеет одно значение. Величина структурированного типа со­стоит из величин других типов. Напри­мер, строка состоит из символов, каж­дый отдельный символ строки имеет свое значение (код). Самым распростра­ненным структурированным типом данных является массив, с которым вы познакомитесь в этом учебном году.

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

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

Подпрограммы

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

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

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

Язык программирования — ин­струмент для решения конкретной задачи. Не существует единственного самого лучшего языка программиро­вания. Для решения задач разного ро­да и уровня сложности требуется при­менять разные языки и технологии программирования. В простейших случаях достаточно освоить основы структурного написания программ, например на языке PascalABC. Для создания же сложных проектов тре­буется не только свободно владеть каким-то языком в полном объеме, но и иметь представление о других язы­ках и их возможностях. Как правило, чем сложнее задача, тем больше вре­мени требуется на освоение инстру­ментов, необходимых для ее решения.

Первым высокоуровневым языком программирования, который был реализован практически, стал в 1949 г.
Краткий код (Short Code). Операции и переменные в этом языке программирования кодировались двухсимвольными
сочетаниями.Пример 2.1. Некоторые языки программирования высокого уровня:

C++; JavaScript;
Python; Perl;
Pascal (Delphi); Fortran;
С#; VisualBasic;
Java; Lisp.

Пример 2.2. При трансляции про­граммы происходит преобразование текста с одного языка на другой. Раз­личают следующие виды трансляции: компиляция и интерпретация.

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

Среда программирования PascalABC имеет встроенный компилятор, опции которого можно настроить, выполнив команду Сервис Настройки:

Интерпретатор — транслятор, кото­рый может работать двумя способами:

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

Чистая интерпретация применяется для языков JavaScript, VBA, Lisp и др. Примеры интерпретаторов, создающих байт-код: Perl, PHP, Python и др.

В 1951 г. американский ученый Грейс Мюррей Хоппер (1906— 1992) создала первый в мире компиля­тор и ввела сам этот термин.

Компилятор осуществлял функцию объединения команд, производил вы­деление памяти компьютера и пре­образование команд высокого уровня (в то время псевдокодов) в машинные команды.

Пример 2.3. Семантическая (стро­ка 4) и синтаксическая (строка 6) ошиб­ки в среде PascalABC.

Роберт В. Флойд (1936—2001) — американский ученый в области теории вычислительных систем. Лауреат пре­мии Тьюринга. Впервые термин «пара­дигма программирования» был приме­нен Р. Флойдом в 1978 г. во время по­лучения премии Тьюринга: «Если про­гресс искусства программирования в целом требует постоянного изобретения и усовершенствования парадигм, то со­вершенствование искусства отдельного программиста требует, чтобы он расши­рял свой репертуар парадигм».

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

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

Пример 2.5. Особенность языков процедурного программирования за­ключается в том, что задачи разбива­ются на шаги и решаются шаг за ша­гом. Решение для каждого отдельного шага оформляется в виде отдельной процедуры. К процедурным языкам от­носят: C, Pascal, Lua и др.

Пример 2.6. В основе функциональ­ных языков лежит лямбда-исчисление (Х-исчисление) — математическая тео­рия для формализации понятия вычис­лимости. К ним относят: Haskell, Lisp, F# и др.

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

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

К объектно-ориентированным язы­кам относят:

  • C++;
  • Java;
  • Delphi;
  • Python;
  • Ruby и др.
Развитие объектно-ориентированного программирования часто связывают с понятиями «событие» (событийно­ориентированное программирование) и «компонент» (компонентное програм­мирование).
Среда PascalABC предоставляет возможность создавать оконные при­ложения. При разработке интерфейса программы используются визуальные компоненты, а программный код осно­ван на событийном программировании. Создавать такие приложения вы научи­тесь в 11-м классе.

Пример 2.8. Мультипарадигменные языки программирования чаще всего поддерживают процедурную (струк­турную), объектно-ориентированную и функциональную парадигмы: C++, Python, JavaScript, Ruby, C#.

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

Пример 2.9. Учебный язык обеспе­чивает простоту, ясность и удобочитае­мость конструкций языка. Как учеб­ные языки программирования разраба­тывались: Basic, Pascal, Logo, Scratch.

Пример 2.10. Некоторые эзотери­ческие языки служат для проверки математических концепций (Thue, Unlambda), другие создаются для раз­влечения. Часто они пародируют «на­стоящие» языки программирования или являются абсурдным воплоще­нием концепций программирования (Smetana, Var’aq, FiM++ и др.).

Пример 2.11. Псевдокод алгоритма нахождения суммы квадратов первых n натуральных чисел.

ввод n;
S = 0
нц для i от 1 до n
S = S + i * i
кц
Служебные (ключевые) слова — за­резервированные слова, которые имеют специальные значения для компилято­ра. Их нельзя использовать как иден­тификаторы в программах.

Пример 2.12. Некоторые служебные слова (в алфавитном порядке) в разных языках программирования.

Пример 2.13. Структурная схема процедурного языка программиро­вания.

Выражения строятся из величин (констант и переменных), функций, скобок, знаков операций и т. д. Тип выражения определяется результатом вычислений. Выражения могут при­нимать числовые, логические, сим­вольные, строковые и другие значе­ния.
Выражение 5 + 3 является число­вым, а выражение А + В может иметь самый разный смысл в зависимости от того, что стоит за идентификато­рами А и В (если А и В — строки, то результат — строка, которая полу­чилась при конкатенации исходных строк).

Пример 2.14. Запись оператора цик­ла (поиск суммы квадратов первых n натуральных чисел).

Пример 2.15. Классификация дан­ных в языке программирования.
Подобная структура типов данных присуща многим языкам программирования. С другими типами данных, которые используются в языке PascalABC, можно познакомиться в справочной системе.
Пример 2.16. Имена переменных за­дает программист. Существуют реко­мендации, как можно (нужно) имено­вать переменные в коде:

  • имя переменной должно быть по­нятным, наглядным и отражать суть обозначаемого объекта (sum, number, count_of_positive);
  • вводить переменным короткие имена (s, i, n) можно в том случае, когда они используются в небольшом фрагменте кода и их применение оче­видно.

Некоторым идентификаторам зара­нее предписан определенный смысл, например sin, cos, sqrt, abs — имена математических функций.

Многие компании разрабатывают свои правила по оформлению кода, в которых прописаны также и правила именования переменных. В компании Microsoft используют так называемую «венгерскую нотацию». Известными являются также:

Правила оформления кода, разрабо­танные в некоторых компаниях, явля­ются открытыми и могут использовать­ся другими разработчиками. Напри­мер, в Google разработаны styleguide («гид по стилю») для разных языков программирования (C++, Java, Python, Lisp и др.).

Пример 2.17. Список стандартных функций языка программирования PascalABC можно посмотреть в спра­вочной системе:
Пример 2.18. Процедуры в языках программирования.
В языке VisualBasic процедуры объ­являются как sub (сокращение от англ. subroutine — подпрограмма).
В языке С++ нет отдельных кон­струкций для описания процедур. Их роль выполняют функции, которые имеют тип void (англ. «пустота»).

На сайте Tiobe ежемесячно публи­куется рейтинг языков программиро­вания.
Рейтинг составляется на основе под­счета результатов поисковых запросов, содержащих название языка, в Google, Blogger, Wikipedia, YouTube, Baidu, Yahoo!, Bing, Amazon.

 

1. Для чего предназначен транслятор?
2. Какие функции выполняет компилятор? Интерпретатор?
3. Что определяется парадигмой программирования?
4. Из каких элементов может состоять алфавит языка программирования?
5. Что представляет собой оператор языка программирования?
6. Какие типы данных вам известны?
7. Для чего используются функции и процедуры?

Упражнения

1. Напишите программы для решения следующих задач.

  1. Определите последнюю цифру натурального числа N.
  2. Два отрезка на плоскости задаются координатами своих концов. Определите, какой из них короче.
  3. Найдите сумму 1 +​\(\frac{1}{2^2}+\frac{1}{3^2}+…+\frac{1}{N^2}\)  для заданного N.
  4. Вводится строка текста. Определите, является ли она палиндромом.
  5. Вводятся два целых числа, являющихся числителем и знаменателем дроби. Сократите дробь, выведите полученные числитель и знаменатель. Подсказка: можно воспользоваться алгоритмом Евклида.
  6. *.Дед Мазай и заяц играют в очень простую игру. Перед ними — гора из N одинаковых морковок. Каждый из игроков во время своего хода может взять из нее любое количество морковок, равное неотрицательной степени числа 2 (1, 2, 4, 8, …). Игроки ходят по очереди. Кто возьмет последнюю морковку, тот и выигрывает. Составьте алгоритм, который при заданном значении N определяет победителя в этой игре. Учтите, что каждый из игроков хочет выиграть и не делает лишних ходов, т. е. играет оптимально.

2.* Предложенные ниже алгоритмы записаны разными способами. Определите, что делает каждый из предложенных алгоритмов, и реализуйте их на языке Pascal.

3.* Изобразите любой алгоритм из упражнения 2 в виде блок-схемы.

 

Проверь себя