Pull to refresh

База по языкам программирования: Как появлялись языки и зачем

Reading time6 min
Views22K
Привет всем. Публикую выдержки из вводного курса нашей компании по промышленному программированию. Если выдержки покажутся интересными хаброобществу, продолжу публиковать другие куски.

Курс этот предназначен прежде всего для junior developer'ов и позволяет повысить уровень аргументации в холиварах на тему «почему PHP (Java, Perl, Bash) отстой».

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

Часть первая: Как появлялись языки и зачем
Часть вторая: Принцип сохранения функционала
Часть третья: Синтаксический сахар или история развития языков


Как появлялись языки и зачем



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

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

Машина Тьюринга

Машина Тьюринга — это бесконечная магнитная лента (вспомните года, когда работал Тьюринг — магнитная лента тогда была очень прогрессивным устройством). Лента разбита на логические ячейки — последовательные отрезки ленты, на которые можно записать тот или иной сигнал (к примеру — ASCII символы).

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

Программа на машине Тьюринга задаётся в виде таблицы переходов: если символ А — то сдвиг влево, если Б — сдвиг вправо, если С — записать в ячейку D и сдвинуться влево, если E — то остановить работу (и так далее, пока символы не кончатся).

Одно из главных определений, задаваемых машиной Тьюринга — это контекст исполнения:
Контекст выполнения программы — совокупность всех данных, определяющих её поведение.

Парадигмы задания правил


Как видно по примеру машины Тьюринга, ЯП манипулируют не данными, а преобразованиями данных. Но правила для обработки как-то надо представлять и определять. На данный момент найдено два способа задания правил для обработки — это императивная и декларативная формы записи правил.

Декларативная форма

Первая форма называется декларативной. Хороший пример этого типа описания преобразований является алгебраическая запись: a принадлежит отрезку [b,c] (постулируем этот факт), d принадлежит прямой (a,b), если выполняется условие d=f(a,b) (тоже постулируем этот факт).

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

Декларативный ЯП описывает не шаги, которые нужно сделать, чтобы получить результат, а то, что требуется получить в качестве результата.

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

Императивная форма

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

Императивная форма: это список приказов, выражаемых в повелительном наклонении. К примеру, присвоить a значение (c-b)/2. Или если a равно b, то перейти к выполнению следующих команд иначе — других команд. Внимание в императивной форме отдаётся не самим данным, а процессу обработки этих данных.

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

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

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

Парадигмы обработки информации

Кроме способа задания правил ЯП так же различаются по своему способу подхода к обрабатываемым данным. Если внимательно посмотреть на информацию, которую преобразуют ЯП, то можно увидеть, что она бывает двух типов: дискретная и непрерывная.

Дискретно-событийная модель

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

Нетрудно заметить, что при дискретном представлении информации цикличность работы программы задаётся самим способом ввода: прочитали байт — сделали обработку — выплюнули результат.
Но что происходит, если кусочки информации не выравнены по своим размерам? К примеру, на вход программы подаётся длинный текст, а программа должна оперировать не буквами-символами, а словами?
Для решения подобных задач вводится понятие события. Событие возникает в ответ на какую-либо распознанную последовательность, прочитанную из источника данных.

Если рассматривать пример с текстом и словами, то мы байт за байтом читаем данные и записываем в пополняемый буфер памяти. И делаем это до тех пор, пока в потоке данных мы не читаем пробел или точку, что будет означать возникновение события “конец слова”.

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

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

Наступление события означает для программы одно очень важное действие: программа меняет своё собственное состояние (контекст выполнения). После наступления события программа может работать исходя из этого знания, что это событие произошло. К примеру, если мы нашли слово “абырвалг”, то теперь можем пользоваться знанием, что оно существует далее, при выполнении хода работ.

Модель непрерывных данных

Подход к программированию — “события случаются и мы должны реагировать на это” повсеместно распространён и кажется сейчас чем-то естественным, но это совсем не так. Многие вещи более естественно описывать в непрерывных данных.

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

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

Оперируя непрерывными данными мы лишаемся понятия события.

Там, где нам необходимы события и порядок выполнения действий над кусочками данных удобнее всего применять дискретно-событийный подход к обработке. Это задачи реакции на события от пользователя, интерпретация и вычленение событий из входящего потока данных, последовательное преобразование кусочков информации.
Там же, где нам нужна картина “в целом” наиболее удобен подход аналитических языков: это получение срезов данных, описание структур данных, решение математических функций, описание графики, непрерывные преобразования одного входящего потока в другой.
Tags:
Hubs:
Total votes 51: ↑35 and ↓16+19
Comments25

Articles