Основы программирования: от алгоритмов до первого кода

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

1. Введение в компьютерные науки: как работают программы и алгоритмы

Введение в компьютерные науки: как работают программы и алгоритмы

Добро пожаловать в курс «Основы программирования». Многие считают, что программирование — это магия, доступная лишь избранным математическим гениям. На самом деле, это навык, похожий на изучение иностранного языка или освоение кулинарного искусства. В этой первой статье мы разберем фундамент, на котором строится весь цифровой мир: что такое компьютерные науки, как «думает» машина и что такое алгоритмы.

Компьютер: не умный, а послушный

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

Компьютерные науки (Computer Science) — это не просто изучение компьютеров, это изучение вычислений и информации. Это наука о том, как заставить машину решать задачи, которые нужны человеку.

Язык нулей и единиц

В основе работы любого современного компьютера лежит двоичная система счисления. Процессор компьютера состоит из миллиардов микроскопических переключателей — транзисторов. У транзистора есть только два надежных состояния: включен (ток идет) и выключен (тока нет). В информатике мы обозначаем эти состояния как 1 и 0.

!Визуализация того, как электрические сигналы превращаются в двоичный код внутри процессора.

Почему именно два состояния? Потому что это надежно. Легко отличить наличие сигнала от его отсутствия. Чтобы понять, сколько информации мы можем закодировать с помощью определенного количества бит (нулей или единиц), используется следующая формула:

Где — это количество уникальных комбинаций (состояний), которые можно закодировать, — основание двоичной системы (всего два символа: 0 и 1), а — количество бит (разрядов). Например, если у нас есть 8 бит (1 байт), мы можем закодировать различных значений (например, символов алфавита или оттенков цвета).

Что такое алгоритм?

Если компьютер — это исполнитель, то алгоритм — это инструкция. Это одно из самых важных понятий в программировании.

> Алгоритм — это точная, конечная последовательность действий, направленная на решение определенной задачи.

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

Свойства хорошего алгоритма

Чтобы компьютер (или человек) мог выполнить алгоритм, он должен обладать следующими свойствами:

  • Дискретность. Процесс разбит на отдельные шаги.
  • Определенность (детерминированность). Каждый шаг должен быть понятен однозначно. Нельзя написать «добавьте соль по вкусу», так как у компьютера нет вкуса. Нужно написать «добавьте 5 грамм соли».
  • Результативность. Алгоритм должен приводить к результату за конечное число шагов.
  • Массовость. Алгоритм должен подходить для решения целого класса задач (например, алгоритм сложения подходит для любых двух чисел, а не только для 3 и 5).
  • !Сравнение рецепта и программного алгоритма для наглядности.

    Как работает программа: Модель IPO

    Любая программа, от простейшего калькулятора до сложной видеоигры, работает по принципу IPO (Input — Process — Output).

  • Input (Входные данные). Информация поступает в систему. Это может быть нажатие клавиши, движение мыши, файл с диска или данные из интернета.
  • Process (Обработка). Компьютер выполняет алгоритм над полученными данными. Он считает, сравнивает, сортирует или преобразует информацию.
  • Output (Выходные данные). Результат обработки возвращается пользователю. Это может быть изображение на экране, звук, распечатанный документ или сохранение файла.
  • Представьте кофемашину: * Input: Вода, кофейные зерна, нажатие кнопки «Капучино». * Process: Нагрев воды, помол зерен, смешивание под давлением. * Output: Чашка горячего кофе.

    От алгоритма к коду

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

    Здесь на сцену выходят языки программирования высокого уровня (Python, Java, C++, JavaScript и другие). Они похожи на английский язык и позволяют нам писать инструкции, понятные человеку.

    Однако процессор не понимает Python. Ему нужен переводчик. Эту роль выполняют специальные программы:

    * Компиляторы. Переводят весь текст программы в машинный код целиком, создавая исполняемый файл (например, .exe). Это похоже на перевод книги: сначала переводим всё, потом читаем. * Интерпретаторы. Переводят и выполняют программу строчка за строчкой. Это похоже на синхронный перевод устной речи.

    Три кита логики программирования

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

    1. Последовательность (Sequence)

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

    Пример:

    2. Ветвление (Selection)

    Это возможность выбора действий в зависимости от условия. В жизни мы используем слово «Если».

    !Простая блок-схема, демонстрирующая принцип ветвления в алгоритмах.

    Пример:

    3. Цикл (Iteration)

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

    Пример:

    Вычислительное мышление

    Главный навык программиста — это не знание синтаксиса (правил написания) языка, а вычислительное мышление (Computational Thinking). Это умение видеть задачу так, как видит её компьютер.

    Оно включает в себя: * Декомпозицию: разбиение сложной задачи на мелкие, простые подзадачи. * Распознавание паттернов: поиск сходства с задачами, которые мы уже решали раньше. * Абстракцию: отбрасывание лишних деталей и концентрация на главном. * Алгоритмизацию: создание пошагового плана решения.

    > «Компьютерные науки так же мало касаются компьютеров, как астрономия касается телескопов». — Эдсгер Дейкстра. [Ссылка: Цитаты известных информатиков]

    Эта фраза означает, что инструмент (компьютер) вторичен. Первично — умение мыслить алгоритмически и структурировать информацию.

    Заключение

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

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

    2. Переменные, типы данных и базовые операторы

    Переменные, типы данных и базовые операторы

    В предыдущей статье мы разобрали, что компьютер — это исполнитель, работающий по модели IPO (Вход — Процесс — Выход). Но чтобы «Процесс» мог состояться, компьютеру нужно где-то хранить данные, с которыми он работает прямо сейчас. Представьте, что вы решаете сложную математическую задачу в уме. Рано или поздно вы забудете промежуточный результат. Чтобы этого не случилось, вы записываете числа на бумаге.

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

    Что такое переменная?

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

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

    !Визуальная метафора переменных как коробок с данными и ярлыками-именами.

    Теперь, когда вам понадобится узнать возраст пользователя, вы не ищете число 25 по всему складу. Вы просто говорите кладовщику (компьютеру): «Принеси мне содержимое коробки с надписью user_age».

    Операция присваивания

    В математике знак равенства () означает, что левая и правая части равны. В программировании это не так. Знак = — это оператор присваивания.

    Рассмотрим выражение:

    Для программиста это читается так: «Взять значение 10 и положить его в переменную с именем score». Направление действия всегда идет справа налево.

    Это позволяет писать конструкции, которые свели бы с ума учителя математики:

    Где: * (слева) — это переменная, в которую мы запишем новый результат. * — оператор присваивания. * (справа) — это текущее значение, которое мы достаем из памяти. * — число, которое мы прибавляем.

    Эта формула означает: «Возьми текущее значение , увеличь его на 1 и сохрани результат обратно в ту же коробку ».

    Типы данных: почему контекст важен

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

    Тип данных определяет:

  • Сколько памяти нужно выделить.
  • Какие операции можно совершать с этими данными.
  • Рассмотрим четыре базовых типа, которые есть почти во всех языках программирования (Python, Java, C++, JavaScript).

    1. Целые числа (Integer)

    В программировании этот тип часто называют int. Это числа без дробной части: -5, 0, 42, 100500. Они используются для счета предметов, индексов, количества итераций.

    2. Числа с плавающей точкой (Float)

    Тип float (или double). Это дробные числа: 3.14, -0.01, 2.0. Обратите внимание: в программировании для разделения целой и дробной части используется точка, а не запятая.

    3. Строки (String)

    Тип str. Это любой текст: «Привет», «Error 404», «user@email.com». Компьютер понимает, что перед ним строка, если данные обернуты в кавычки (одинарные или двойные).

    > Важно: 100 — это число. `

    3. Управление потоком выполнения: условные конструкции и циклы

    Управление потоком выполнения: условные конструкции и циклы

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

    Но реальная жизнь — это не прямая линия. Это постоянный выбор («Если пойдет дождь, я возьму зонт») и повторение действий («Я буду мешать суп, пока он не закипит»). Чтобы научить компьютер действовать так же гибко, нам нужно освоить управление потоком выполнения (Control Flow).

    Сегодня мы разберем два фундаментальных кита логики программирования: условные конструкции (ветвления) и циклы.

    Логика — основа принятия решений

    Прежде чем переходить к коду, нужно понять, как компьютер принимает решения. В отличие от человека, который может ответить «наверное» или «скорее да, чем нет», компьютер оперирует только абсолютной истиной или абсолютной ложью.

    В прошлой лекции мы упоминали тип данных Boolean (булево значение). У него всего два состояния:

    * True (Истина, 1) * False (Ложь, 0)

    Любое решение в программе сводится к ответу на вопрос, который подразумевает только «Да» или «Нет».

    Операторы сравнения

    Чтобы получить True или False, мы часто сравниваем значения. Вот основные инструменты для этого:

    * == (Равно). Важно: не путайте с = (присваивание). a == b спрашивает «равно ли a значению b?», а a = b приказывает «сделать a равным b». * != (Не равно). * > (Больше) и < (Меньше). * >= (Больше или равно) и <= (Меньше или равно).

    С точки зрения математической логики, результат сравнения можно записать так:

    Где — это результат (Истина или Ложь), — первое сравниваемое число, — оператор сравнения «больше», а — второе число. Если действительно больше , то станет Истиной.

    Условные конструкции: Если... То... Иначе

    Условная конструкция (Conditional Statement) позволяет программе выбрать один из нескольких путей выполнения кода в зависимости от условия.

    !Классическая блок-схема, демонстрирующая выбор действия в зависимости от выполнения условия.

    Конструкция if (Если)

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

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

    Конструкция if-else (Если-Иначе)

    Часто нам нужно не просто пропустить действие, а выполнить альтернативное действие.

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

    Конструкция else-if (Иначе-Если)

    В жизни вариантов часто больше двух. Для этого используется цепочка проверок. В Python это записывается как elif.

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

    Логические операторы: И, ИЛИ, НЕ

    Иногда одного условия недостаточно. Например, чтобы получить кредит в банке, нужно: (Возраст > 18) И (Доход > 50000). Для объединения условий используются логические операторы.

    1. Логическое И (AND)

    Результат будет истинным, только если оба условия истинны.

    Математически это записывается как конъюнкция:

    Где — итоговый результат, — первое условие, — символ логического И (AND), — второе условие. будет истиной только тогда, когда и , и являются истиной.

    2. Логическое ИЛИ (OR)

    Результат будет истинным, если хотя бы одно из условий истинно.

    Математически это дизъюнкция:

    Где — итоговый результат, — первое условие, — символ логического ИЛИ (OR), — второе условие. будет истиной, если истинно , или истинно , или истинны оба.

    3. Логическое НЕ (NOT)

    Этот оператор просто инвертирует значение. Истину превращает в Ложь, а Ложь — в Истину.

    Где — результат, — символ логического отрицания (NOT), — исходное условие. Если — Истина, то — Ложь.

    Циклы: Сила повторения

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

    Для многократного выполнения одного и того же блока кода используются циклы (Loops). Одно выполнение тела цикла называется итерацией.

    !Визуальная метафора цикла: действие повторяется до достижения результата.

    Цикл while (Пока)

    Этот цикл работает до тех пор, пока условие остается истинным. Он похож на многократно повторяющийся if.

    Как это работает:

  • Компьютер проверяет: энергия больше 0?
  • Да (10 > 0). Выполняем бежать(). Отнимаем 1 от энергии.
  • Снова проверяем: энергия больше 0?
  • Да (9 > 0). Повторяем...
  • ...Процесс идет, пока энергия не станет равна 0. Условие 0 > 0 станет ложным (False), и цикл остановится.
  • > Осторожно: Бесконечный цикл! > Если вы забудете уменьшать энергию внутри цикла, условие 10 > 0 будет истинным вечно. Программа зависнет, бесконечно выполняя команду бежать(). Это одна из самых частых ошибок новичков.

    Цикл for (Для)

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

    Здесь переменная номер_круга будет автоматически менять свое значение на каждой итерации: сначала 1, потом 2, и так до 5. Программисту не нужно вручную следить за условием выхода — цикл сам остановится, когда закончатся элементы в списке.

    Вложенные конструкции

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

    Пример алгоритма поиска красного яблока в корзине:

    Заключение

    Сегодня мы превратили наши программы из простых калькуляторов в системы, способные принимать решения и автоматизировать рутину. Мы изучили:

  • Логический тип данных: Истина и Ложь.
  • Условные конструкции: Как научить программу выбирать путь (if, else).
  • Логические операторы: Как комбинировать условия (AND, OR, NOT).
  • Циклы: Как повторять действия эффективно (while, for).
  • Теперь у вас есть полный набор инструментов для написания алгоритмов любой сложности. Но что делать, если код становится слишком большим и запутанным? Как не потеряться в тысячах строк условий и циклов? Об этом мы поговорим в следующей статье, посвященной функциям и модульности — искусству разделения больших задач на маленькие детали.

    4. Функции, модульность и повторное использование кода

    Функции, модульность и повторное использование кода

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

    Если вы просто скопируете эти 10 строк для каждой комнаты, ваш код превратится в огромную, запутанную «простыню». А если вы обнаружите ошибку в алгоритме включения? Вам придется исправлять её в четырех разных местах. Вероятность забыть одно из них стремится к 100%.

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

    Что такое функция?

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

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

    Другая аналогия — кухонный комбайн. У него есть имя (например, «Блендер»), отверстие для загрузки продуктов (входные данные) и результат работы (выходные данные).

    !Визуализация принципа работы функции: вход, процесс, выход.

    Анатомия функции

    Создание функции обычно состоит из двух этапов: объявление (описание того, что она делает) и вызов (использование).

    Рассмотрим пример на псевдокоде (похожем на Python):

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

    Параметры и аргументы: общение с функцией

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

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

    Здесь name — это переменная-параметр. Теперь мы можем здороваться с кем угодно:

    * Параметр — это переменная, указанная при создании функции (в скобках). * Аргумент — это конкретное значение, которое мы передаем в функцию при вызове («Алексей», «Мария»).

    Возвращаемые значения: результат работы

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

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

    Где — искомая площадь, — математическая константа (примерно 3.14159), а — радиус круга.

    В коде это будет выглядеть так:

    Важное отличие: * print() — это как показать картинку другу. Он её увидел, но взять не может. * return — это как передать другу яблоко. Он может его взять, положить в карман (сохранить в переменную) или съесть (использовать в другом вычислении).

    Принцип DRY: Не повторяйся

    В программировании существует священное правило, известное как DRYDon't Repeat Yourself (Не повторяй самого себя).

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

    Почему дублирование кода — это плохо?

  • Читаемость. Код становится огромным и сложным для восприятия.
  • Отладка. Если в повторяющемся коде есть ошибка, её нужно исправлять во всех копиях.
  • Масштабируемость. Если нужно изменить логику, придется править везде.
  • Функции позволяют нам следовать принципу DRY, делая код чистым и поддерживаемым.

    Область видимости (Scope): Что происходит в Вегасе...

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

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

    Модульность: Разделяй и властвуй

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

    Представьте, что вы пишете игру. Задача «Создать игровой мир» звучит пугающе. Но если разбить её на функции, всё становится проще:

    * generate_terrain() (создать ландшафт) * spawn_enemies() (разместить врагов) * draw_sky() (нарисовать небо)

    Каждую из этих функций можно писать и тестировать отдельно. Это и есть модульность.

    !Метафора модульности: сложная структура собирается из простых, независимых блоков.

    Библиотеки: Использование чужого кода

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

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

    Программирование сегодня — это не столько написание кода с нуля, сколько умение грамотно комбинировать свои функции с готовыми решениями.

    Заключение

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

  • Не повторять код (DRY).
  • Структурировать сложные задачи (Модульность).
  • Изолировать данные (Область видимости).
  • Теперь вы знаете, как управлять потоком программы и как организовывать код. Но как эффективно хранить и обрабатывать большие объемы данных, например, список из тысячи пользователей или каталог товаров? Для этого нам понадобятся структуры данных, о которых мы поговорим в следующей статье.

    5. Основы структур данных и работа с массивами

    Основы структур данных и работа с массивами

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

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

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

    Что такое массив?

    > Массив (Array) — это структура данных, хранящая набор элементов одного типа, расположенных в памяти последовательно друг за другом.

    Вернемся к аналогии с коробками. Если переменная — это отдельная коробка на складе, то массив — это длинный стеллаж с пронумерованными ячейками. Или, если хотите, таблетница с отсеками на каждый день недели. У всего стеллажа есть одно имя (например, scores), но чтобы добраться до конкретного значения, нам нужно знать номер ячейки.

    !Визуализация массива как ряда пронумерованных ячеек памяти.

    Как массив выглядит в памяти?

    Главная особенность массива — непрерывность. Когда вы просите компьютер создать массив из 5 целых чисел, он ищет в оперативной памяти свободный блок, в который поместятся ровно 5 чисел подряд. Никаких «дырок» или разрывов.

    Именно благодаря этому компьютер может мгновенно получать доступ к любому элементу. Это чистая математика. Зная адрес начала массива, компьютер вычисляет адрес нужного элемента по формуле:

    Где: * — адрес искомого элемента в памяти. * — базовый адрес (адрес самого первого элемента массива). * — индекс (номер) элемента, который нам нужен. * — размер памяти, занимаемый одним элементом (например, 4 байта для целого числа).

    Благодаря этой формуле компьютеру не нужно перебирать весь массив, чтобы найти 100-й элемент. Он просто подставляет числа в формулу и сразу «прыгает» в нужную ячейку памяти.

    Индексация: Почему программисты считают с нуля?

    В большинстве языков программирования (Python, Java, C++, JavaScript) нумерация элементов массива начинается с нуля. Первый элемент имеет индекс 0, второй — 1, и так далее.

    Это часто сбивает с толку новичков. Почему не с 1? Ответ кроется в формуле, которую мы разобрали выше. Индекс — это не «номер по порядку», а смещение (offset) относительно начала.

    * Чтобы попасть в первый элемент, нам нужно сместиться на 0 шагов от начала. * Чтобы попасть во второй элемент, нужно сместиться на 1 шаг.

    Если у нас есть массив fruits (фрукты):

    То доступ к элементам будет выглядеть так:

    * fruits[0] — это "Яблоко" * fruits[1] — это "Банан" * fruits[3] — это "Груша"

    > Важно: Попытка обратиться к элементу fruits[4] вызовет ошибку (Index Out Of Bounds), так как в массиве всего 4 элемента, и последнего зовут 3.

    Основные операции с массивами

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

    1. Чтение (Access) — Мгновенно

    Как мы выяснили, получение элемента по индексу происходит мгновенно. Неважно, массив длиной в 10 элементов или в 10 миллионов — время доступа будет одинаковым. В информатике это называется O(1) или константное время.

    2. Изменение (Update) — Мгновенно

    Записать новое значение в известную ячейку (fruits[1] = "Киви") так же быстро, как и прочитать его.

    3. Поиск (Search) — Медленно

    Если вы не знаете индекс, а хотите найти «Где лежит Апельсин?», компьютеру придется проверять ячейки по одной: «Это апельсин? Нет. Это апельсин? Нет. Это апельсин? Да!».

    В худшем случае (если искомый элемент последний или его нет вовсе) придется просмотреть весь массив. Это называется линейный поиск.

    4. Вставка и Удаление (Insert / Delete) — Очень медленно

    Это ахиллесова пята массивов. Представьте, что 10 человек сидят на скамейке плотно друг к другу. Чтобы посадить нового человека посередине (между 3-м и 4-м), всем, кто сидит справа от 3-го, нужно встать и подвинуться на одно место вправо.

    !Иллюстрация сложности вставки элемента в середину массива.

    В памяти компьютера происходит то же самое. Чтобы вставить элемент в начало массива, нужно скопировать и сдвинуть все существующие элементы на одну ячейку вперед. Это ресурсоемкая операция.

    Статические и Динамические массивы

    В классических языках (как C) массивы статические. Это значит, что при создании вы обязаны сказать: «Мне нужно место ровно под 10 чисел». Изменить этот размер потом нельзя. Если нужно сохранить 11-е число — создавайте новый, больший массив и копируйте туда всё вручную.

    Современные языки высокого уровня (Python, JavaScript) используют динамические массивы (в Python они называются списками — list).

    Они умеют «растягиваться». Вы можете просто добавлять элементы:

    Магия под капотом: На самом деле память компьютера не резиновая. Когда вы создаете динамический массив, язык программирования выделяет место «с запасом». Когда запас заканчивается, он автоматически:

  • Создает новый, больший кусок памяти (обычно в 2 раза больше).
  • Копирует туда все старые данные.
  • Удаляет старый массив.
  • Это происходит незаметно для вас, но помнить об этом полезно для понимания производительности.

    Многомерные массивы

    Данные не всегда выстраиваются в одну линию. Иногда нам нужны таблицы. Например, игровое поле в шахматах или пиксели на экране монитора.

    Для этого используются многомерные массивы (массивы массивов).

    Представьте таблицу Excel. У нее есть строки и столбцы. Чтобы получить значение, нам нужны две координаты: номер строки и номер столбца.

    Разберем matrix[1][2]:

  • Сначала мы берем элемент с индексом 1 из внешнего массива. Это список [4, 5, 6].
  • Затем внутри этого списка мы берем элемент с индексом 2. Это число 6.
  • В памяти компьютера многомерный массив всё равно хранится как длинная непрерывная лента, но язык программирования дает нам удобный способ работать с ним как с таблицей.

    Строки — это тоже массивы

    В большинстве языков строка (String) — это просто массив символов. Именно поэтому мы можем обращаться к буквам по индексу:

    Однако есть нюанс: во многих языках (включая Python и Java) строки неизменяемы (immutable). Вы не можете написать name[0] = 'B', чтобы превратить "Alice" в "Blice". Вам придется создать новую строку целиком.

    Итерация по массиву

    Самая частая задача программиста — сделать что-то с каждым элементом массива. Например, увеличить все цены на 10% или найти среднюю оценку.

    Для этого идеально подходит цикл for, который мы изучили ранее.

    Этот код проходит по массиву от начала до конца, по очереди помещая каждый элемент в переменную price.

    Заключение

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

    В следующей статье мы поговорим о том, как оценивать эффективность наших программ не «на глазок», а научно. Мы познакомимся с понятием алгоритмической сложности и узнаем, что такое Big O Notation.