3.3 Как появились языки программирования

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

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

Первый язык: язык ассемблера

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

Это важно: ответив на эти вопросы, мы сможем двинуться дальше и увидеть новые ограничения, которые привели к появлению уже современных языков программирования.

Но обо всём по порядку. Для начала давайте вспомним, что такое язык ассемблера.

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

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

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

2.2

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

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

У истоков этой идеи стояли Кэтлин и Эндрю Бут. Работая в Лондоне в конце 1940-х, они пытались облегчить программирование своей экспериментальной машины ARC. Вместо того чтобы заставлять программистов запоминать двоичные комбинации, они предложили сокращённую символическую нотацию. Кэтлин Бут описала её в 1947 году в руководстве Coding for ARC, и это стало одним из первых шагов к тому, что мы сегодня называем языком ассемблера.

Сокращённая символьная нотация выглядела так:

A 44 # сложить число с числом в ячейке памяти 44.

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

То есть они придумали саму идею, как упростить написание программ, и в этом их огромная заслуга.

Параллельно в Кембридже Морис Уилкс вместе с коллегами Дэвидом Уилером и Стэнли Гиллом создавал компьютер EDSAC. Уилкс видел, что компьютер будет полезен лишь тогда, когда его смогут программировать не только разработчики самой машины, но и широкое сообщество учёных.

Уилер придумал initial orders (первоначальные указания) — простейшую систему загрузки и символического кодирования, которая уже автоматически переводила текстовые команды в машинный код для EDSAC. То есть её можно назвать первым ассемблером для EDSAC.

Гилл участвовал в создании первых библиотек подпрограмм, а вместе с Уилксом и Уилером написал книгу The Preparation of Programs for an Electronic Digital Computer (1951), где впервые подробно объяснялось, как использовать ассемблер и что он даёт программисту. Эта книга стала для многих программистов того времени настольным руководством и проводником из мира машинного кода в мир символических языков.

А как вообще работала система initial orders на EDSAC?

EDSAC (1949) был уже электронным, а не электромеханическим компьютером и работал на вакуумных лампах (их было около 3000).

У него не было энергонезависимой памяти или встроенной микропрограммы. Чтобы машина могла вообще что-то загрузить с перфоленты, в неё нужно было ввести самую первую программу-загрузчик — initial orders.

Грубая параллель: это что-то вроде системного промпта для GPT, который описывает инструкции, как модели обрабатывать последующую информацию.

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

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

То есть система initial orders играла роль первичного загрузчика (в современных компьютерах это BIOS) и мини-ассемблера одновременно.

Как видите, ассемблер связывал два мира: символические обозначения и машинные инструкции, а также человеческие алгоритмы и конкретные адреса в памяти.

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

Так ассемблер стал переходным мостом. Супруги Бут заложили фундамент идеи. Уилкс, Уилер и Гилл сделали её практическим инструментом на EDSAC. А дальше на этой почве выросли языки более высокого уровня. Но именно ассемблер впервые показал: с машиной можно говорить не только цифрами, а ещё и словами.

От низкоуровневых к высокоуровневым языкам

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

В 1940–50-е годы таких языков появилось три. Одному из них не суждено было развиться, другой оказался требователен к ресурсам, а третий используется до сих пор. Расскажем о каждом чуть подробнее — по порядку.

Plankalkül

Это первый высокоуровневый язык, который разработал уже знакомый нам Конрад Цузе.

Plankalkül поддерживал абстракции: массивы, логические выражения, даже подпрограммы и условные конструкции.

Пример кода

Но Цузе и его детищу сильно не повезло: сначала учёный работал в изоляции в нацистской Германии, а потом — в условиях послевоенной разрухи, когда основным потенциальным заказчикам (военным и промышленности) было не до инноваций.

У Цузе не было ни аппаратного, ни академического фундамента, чтобы воплотить свою идею в жизнь. Разработанный им компьютер Z3 был несовместим с новым языком. Так что до 1972 года Plankalkül существовал просто на бумаге, пока энтузиасты не обнаружили и не переиздали записи Цузе.

Цузе создал язык будущего, но в эпоху, которая не была к этому готова — ни технологически, ни академически, ни социально.

Short Code

Разработан физиком Джоном Моукли в 1949 году. Использовался на компьютерах UNIVAC и BINAC.

Он представлял собой макроязык: каждая инструкция на Short Code интерпретировалась в несколько операций на машинном коде. Это решало проблему скорости разработки.

Напомните, что за интерпретация и компиляция?

Это подходы к выполнению программ процессором. И сами программы, которые обрабатывают код.

Сейчас будет очень грубая аналогия. Представьте: Васе и Пете нужно прочитать стих с выражением.

У Васи было 10 минут на подготовку. Вася прочитал стих несколько раз про себя и вслух, прочувствовал структуру, пометил ударения, потренировался, вышел на сцену, быстро прочитал и ушёл под гром аплодисментов. Вася — компилятор.

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

Интерпретатор видел код на Short Code впервые в жизни, и ему в реальном времени требовалось распознать команды, разобрать структуру и перевести всё в инструкции для процессора.

Всё это занимало время — он был в десятки раз медленнее ассемблера. Во многом это помешало ему стать массовым языком, и Short Code так и остался интересным экспериментом.

Пример программы на Short Code

Fortran

Создавался с середины 1950-х годов в компании IBM под руководством Джона Бэкуса. В 1957 году появился компилятор для этого языка, позволявший перевести код, понятный пользователям, на машинный язык.

Fortran поддерживал циклы, массивы и подпрограммы, что делало его очень выразительным для своего времени. Но что ещё важнее — он генерировал код, близкий по скорости к ассемблеру. Это сделало его удобной и эффективной альтернативой. А программисты были готовы пожертвовать небольшой просадкой в производительности ради удобства.

Поддержка IBM помогла организовать разработку документации и стандартизации — и это увеличивало популярность Fortran. Уже к началу 1960-х годов было создано более 40 компиляторов Fortran для большинства распространённых ЭВМ. Таким образом, Fortran стал первым кросс-платформенным, широко распространённым языком программирования.

Он используется и сегодня: в задачах линейной алгебры, численного моделирования, симуляций физических процессов. Его применяют в аэрокосмической сфере (NASA, Boeing), энергетике (в том числе ядерной) и строительной отрасли.

Программа на FORTRAN-77 с выводом компилятора (машина: Control Data CDC 175 в Рейнско-Вестфальском техническом университете Ахена). Источник

Высокоуровневые языки

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

Параллельно покажем, как одна и та же задача решается на разных языках программирования.

В качестве демонстрационной задачи возьмём расчёт факториала — последовательного произведения всех чисел до указанного числа. Например, если пользователь ввёл 5, то факториал будет 120:

5! = 5 × 4 × 3 × 2 × 1 = 120.

Читать рассказ о каждом языке необязательно. Мы собрали всю информацию о них и сферах их применения в отдельной таблице внизу раздела.

Первый язык, о котором хочется рассказать, — Algol.

Algol (Algorithmic Language) был разработан международной группой учёных под руководством Питера Наура, Фридриха Бауэра и уже знакомого нам Джона Бэкуса в 1958–1960 годах.

Их главной целью было создать язык для описания алгоритмов и научных вычислений. Algol впервые ввёл структурное программирование и блоковую структуру с использованием begin…end, что позволило ясно организовывать программы. Язык был заточен на научные и инженерные задачи, особенно на обработку числовых данных и разработку алгоритмов.

Пример кода (Algol 60)
1begin
2  integer procedure factorial(n);
3  value n;
4  integer n;
5  begin
6    if n = 0 then factorial := 1
7    else factorial := n * factorial(n - 1)
8  end;
9
10  integer n;
11  read(n);
12  print(factorial(n))
13end

Параллельно с этим крупный бизнес и Министерство обороны Соединённых Штатов Америки увидели рост расходов на услуги программистов, владеющих разными языками. Чтобы решить эту проблему, разработали язык, с которым должны поставляться все компьютеры для бизнеса и военных нужд.

Так в 1959 году отраслевой комитет, в который вошли представители крупных компаний, государства и военных, представил COBOL (Common Business-Oriented Language). Язык создавался специально для бизнес-вычислений, обработки больших объёмов данных и работы с бухгалтерскими и управленческими системами.

Комитет назывался CODASYL, а координировала участников Грейс Хоппер, подробнее мы расскажем о ней в третьей главе хендбука.
COBOL предложил удобный синтаксис, близкий к английскому языку, что облегчало чтение и поддержку программ. Язык хорошо интегрировался с системами обработки данных на мейнфреймах. Собственно, поэтому он хорошо подошёл для банковских систем, государственных баз данных и корпоративных бухгалтерских приложений.

Причём он до сих пор остаётся востребованным!

Пример кода (COBOL)
1IDENTIFICATION DIVISION.
2PROGRAM-ID. Factorial.
3
4DATA DIVISION.
5WORKING-STORAGE SECTION.
601 N          PIC 9(4).
701 RESULT     PIC 9(10) VALUE 1.
801 I          PIC 9(4).
9
10PROCEDURE DIVISION.
11    DISPLAY "Enter a number: "
12    ACCEPT N
13    MOVE 1 TO I
14    PERFORM UNTIL I > N
15        MULTIPLY RESULT BY I
16        ADD 1 TO I
17    END-PERFORM
18    DISPLAY "Factorial is: " RESULT
19    STOP RUN.

BASIC (Beginner’s All-purpose Symbolic Instruction Code) был разработан Джоном Кемени и Томасом Курцем в 1964 году в Дартмутском колледже. Он создавался для обучения студентов гуманитарных дисциплин основам программирования и позволял быстро писать простые программы.

BASIC отличается простотой синтаксиса и интерактивной интерпретацией, что облегчало освоение языка новичками. Язык был широко распространён на персональных компьютерах 1970–80-х годов.

Пример кода (QBasic)
1INPUT "Enter a number: ", n
2fact = 1
3FOR i = 1 TO n
4    fact = fact * i
5NEXT i
6PRINT "Factorial is "; fact

Simula был создан норвежскими учёными Оле-Йоханом Далем и Кристеном Нюгором для решения задач имитационного моделирования. В 1967 году появилась усовершенствованная версия — Simula 67, которая ввела в практику революционные идеи: классы, объекты, наследование и виртуальные методы.

Всё это положило начало концепции объектно-ориентированного программирования, которая применяется до сих пор.

Пример кода (Simula 67)
1begin
2   integer i, n, fact;
3
4   n := 5;
5   fact := 1;
6
7   for i := 1 step 1 until n do
8      fact := fact * i;
9
10   outint(fact, 10);
11   outimage;
12end

Pascal разработан Никлаусом Виртом в 1970 году как язык для обучения структурированному программированию.
Он ввёл строгую типизацию и поддержку модульного программирования, что способствовало разработке надёжных и понятных программ.

Язык ориентирован на учебные цели и создание приложений средней сложности. Pascal использовался в образовательных средах, научных вычислениях и первых коммерческих программах, таких как системное ПО и утилиты. Его синтаксис и подход оказали влияние на последующие языки, включая Modula и Delphi.

Пример кода (Pascal)
1program Factorial;
2var
3  n, i, result: integer;
4begin
5  write('Enter a number: ');
6  readln(n);
7  result := 1;
8  for i := 1 to n do
9    result := result * i;
10  writeln('Factorial is: ', result);
11end.

Язык C был разработан Деннисом Ритчи в 1972 году в Bell Labs для создания операционной системы Unix.
Этот язык предложил низкоуровневый контроль памяти и мощный набор операторов, что сочетало эффективность машинного кода с абстракцией высокого уровня. Этот язык универсален и применяется широко — от системного программирования до разработки приложений с высокой производительностью.

Знаковые примеры использования — ядро Unix, компиляторы, драйверы устройств, встроенные системы. Язык C оказал фундаментальное влияние почти на все современные языки, включая C++, Java и C#.

Пример кода (C)
1# include <stdio.h>
2
3int main() {
4    int n, i;
5    unsigned long long fact = 1;
6    printf("Enter a number: ");
7    scanf("%d", &n);
8    for(i = 1; i <= n; ++i)
9        fact *= i;
10    printf("Factorial is: %llu\n", fact);
11    return 0;
12}

C++ в 1983 году разработал сотрудник Bell Labs Бьёрн Страуструп как расширение C с поддержкой объектно-ориентированного программирования.

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

Знаковые примеры — Adobe Photoshop, игровой движок Unreal Engine, операционные системы, браузеры.

Пример кода (C++)
1# include <iostream>
2using namespace std;
3
4int main() {
5    int n;
6    unsigned long long fact = 1;
7    cout << "Enter a number: ";
8    cin >> n;
9    for (int i = 1; i <= n; ++i)
10        fact *= i;
11    cout << "Factorial is: " << fact << endl;
12    return 0;
13}

Lua разработали бразильские инженеры Роберту Иерузалимски, Луиш Энрике ди Фигейреду и Валдемар Селиш в 1993 году.

Он создавался как лёгкий расширяемый язык скриптов для встраивания в приложения и игры. Lua поддерживает процедурное, объектное и функциональное программирование, легко интегрируется с C/C++.

Язык ориентирован на расширение функционала приложений и игровых движков. Поэтому он используется в игровой индустрии, например в World of Warcraft (скрипты интерфейса), Angry Birds, скриптах в Roblox.

Пример кода (Lua)
1function factorial(n)
2    if n < 0 then
3        return nil, "Факториал отрицательного числа не определён"
4    elseif n == 0 then
5        return 1
6    else
7        local result = 1
8        for i = 2, n do
9            result = result * i
10        end
11        return result
12    end
13end

Java был разработан Джеймсом Гослингом и командой Sun Microsystems в 1995 году.

Главная инновация — платформа «Write once, run anywhere», которая обеспечивала независимость от аппаратной архитектуры через виртуальную машину JVM (Java Virtual Machine).

Java поддерживает объектно-ориентированное программирование, сборку мусора и многопоточность. Используется для разработки корпоративных приложений, веб-сервисов, мобильных приложений (Android). Примеры: серверные системы, корпоративные ERP, Android-приложения.

Пример кода (Java)
1import java.util.Scanner;
2
3public class Factorial {
4    public static void main(String[] args) {
5        Scanner sc = new Scanner(System.in);
6        System.out.print("Enter a number: ");
7        int n = sc.nextInt();
8        long fact = 1;
9        for (int i = 1; i <= n; i++)
10            fact *= i;
11        System.out.println("Factorial is: " + fact);
12    }
13}

Уже с середины 1980-х годов стала широко распространяться клиент-серверная архитектура (о ней — в самом конце главы), а с начала 1990-х — интернет.

Поэтому разработчикам потребовались новые подходы в языках программирования — так появились языки для серверов (PHP, Python) и языки для клиентов (JavaScript).

Расскажем о них подробнее.

Веб-программирование

Python был создан Гвидо ван Россумом в 1991 году. Этот язык сочетает интерпретацию, динамическую типизацию и богатую стандартную библиотеку.

Python универсален: применяется в направлениях от анализа данных и веб-разработки до автоматизации и научных вычислений. А ещё разработчики любят его за лаконичность и свой особый путь решения задач, который также известен как Zen of Python.

Python применялся при разработке YouTube, а также инструментов машинного обучения и научных библиотек — NumPy, Pandas, TensorFlow.

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

Пример кода (Python)
1n = int(input("Enter a number: "))
2fact = 1
3for i in range(1, n + 1):
4    fact *= i
5print("Factorial is:", fact)

PHP (Hypertext Preprocessor) в 1995 году создал Расмус Лердорф для динамической генерации веб-страниц.
Язык предложил встроенные функции для работы с HTTP, базами данных и HTML. PHP ориентирован на веб-разработку и создание интерактивных сайтов. Он применялся при разработке WordPress и Википедии.

Пример кода (PHP)
1<?php
2function factorial(int $n): int {
3    if ($n < 0) {
4        throw new InvalidArgumentException("Факториал отрицательного числа не определён");
5    } elseif ($n === 0) {
6        return 1;
7    } else {
8        $result = 1;
9        for ($i = 2; $i <= $n; $i++) {
10            $result *= $i;
11        }
12        return $result;
13    }
14}
15

Напоследок поговорим о JavaScript.

В 1995 году этот язык разработал Брендан Эйк из компании Netscape для интерактивных элементов на веб-страницах.

Он выполняется как в браузере, так и на сервере (с помощью среды Node.js) и поддерживает объектно-ориентированное, функциональное и событийное программирование.

Используется на 99% сайтов, без него в веб-разработке никуда.

Пример кода (JavaScript)
1function factorial(n) {
2  if (n < 0) return undefined;
3  let result = 1;
4  for (let i = 2; i <= n; i++) {
5    result *= i;
6  }
7  return result;
8}

А чтобы вам было удобно их сравнивать, мы собрали всю информацию в таблице:

2.2

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



Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Предыдущий параграф3.2. Зачем нужна операционная система и как она работает
Следующий параграф3.4. Как устроены интерфейсы