Базовый CS с нуля
Мутация и состояние
Ты знаешь, как объявить переменную и как присваивание перезаписывает ячейку. Теперь сделай шаг назад и спроси: как работающая программа выглядит внутри машины в любой конкретный момент?
В каждый момент времени каждая переменная хранит какое-то значение. Счёт равен 10. Счётчик жизней равен 3. Флаг конца игры — false. Эти значения вместе описывают то, чем программа является прямо сейчас — не тем, чем она была секунду назад, и не тем, чем будет следующей. Прямо сейчас, в этот момент: этот набор содержимого ячеек.
Этот набор называется состоянием программы. Изменение любого из этих значений — перезапись ячейки — называется мутацией. Эти два слова встречаются везде в информатике и разработке программного обеспечения. Понимание того, что именно они означают в рамках модели памяти, которую ты строишь, делает их точными, а не расплывчатыми.
После этого урока ты сможешь определить состояние программы как снимок значений переменных, объяснить мутацию как изменение на месте в ячейке, а также рассуждать о том, почему состояние и мутация являются центральными понятиями в проектировании программного обеспечения.
Состояние программы: снимок всех текущих значений. В любой момент времени каждая живая переменная имеет текущее значение — паттерн битов, лежащий в её привязанной ячейке памяти в данный момент. Состояние программы (или просто «состояние») — это полная совокупность этих текущих значений.
Думай о состоянии как о фотографии всех ячеек в один момент времени. Если сделать
такую фотографию в момент T, ты увидишь: score = 10, lives = 3,
isGameOver = false. Миллисекунду спустя, после присваивания, фотография выглядит
иначе: score = 15. Состояние — всегда про сейчас, это текущий снимок.
Слово «состояние» (от англ. state) происходит от того же корня, что и «статус»: условие чего-либо в конкретный момент. Состояние программы — это её текущее условие, выраженное набором значений, которые хранят её переменные.
Мутация: изменение значения на месте. Когда присваивание перезаписывает ячейку — когда паттерн битов по некоторому адресу меняется — это изменение называется мутацией. Ячейка существует по тому же адресу до и после; изменилось только её содержимое. Мутация произошла на месте.
«На месте» — ключевая фраза. Мутация не создаёт новую ячейку и не копирует данные. Она изменяет существующую ячейку напрямую. Старое значение исчезло, перезаписанное в том же физическом месте. Адрес ячейки не изменился; содержимое ячейки — другое.
Каждое написанное тобой присваивание (score = score + 5) — это мутация: оно
изменяет значение внутри уже существующей ячейки.
Почему это работает
Почему программисты используют слово «мутация», а не просто «изменение»? Слово несёт специфическую коннотацию из биологии: мутация изменяет что-то изнутри, на месте, а не заменяет это чем-то совершенно новым. В программировании это различие важно: мутация имеет побочные эффекты — другие части программы, которые могут писать в ту же ячейку, увидят изменённое значение. Это противопоставляется неизменяемым (immutable) данным, где вместо изменения значения создаётся новое значение, а исходное остаётся нетронутым. Биологическая метафора выбрана намеренно.
Состояние как сумма всех мутаций на данный момент. Программа хранит не одно значение — она одновременно хранит много переменных. Состояние — совокупность всех их текущих значений. Каждая мутация изменяет один элемент этой совокупности.
Представь игровой цикл. Каждый кадр:
- Игрок движется →
playerXмутирует. - Враг уничтожен →
enemyCountмутирует. - Счёт обновляется →
scoreмутирует. - Время идёт →
elapsedMsмутирует.
После каждого кадра состояние отличается от предыдущего. Состояние — это текущий итог всего, что произошло до сих пор, закодированный в текущих значениях всех переменных.
Вот почему слово «состояние» важно: оно передаёт идею о том, что условие программы в любой момент является накопленным результатом всех предшествующих мутаций.
Программы с состоянием и вычисления без состояния. Не каждое вычисление требует мутации. Функция, принимающая два числа и возвращающая их сумму, не изменяет ни одной ячейки — она просто вычисляет и возвращает результат. Это вычисление без состояния (stateless): оно зависит только от своих входных данных, а не от накопленной истории.
Программа с состоянием (stateful), напротив, хранит значения на протяжении нескольких шагов и изменяет их со временем. Игры, пользовательские интерфейсы, серверы, обрабатывающие запросы, — все они имеют состояние: накапливают историю в переменных, и их поведение в любой момент зависит от того, что произошло раньше.
Умение различать проектирование с состоянием и без состояния — один из фундаментальных навыков в разработке программного обеспечения. Код без состояния легче тестировать (дай входные данные, проверь вывод, нет памяти о предыдущих вызовах). Код с состоянием мощнее, но труднее для понимания.
Частая ошибка
«Состояние» и «мутация» иногда используют расплывчато, имея в виду просто «переменные
существуют». Но точное значение уже: состояние — это текущие значения переменных
в конкретный момент времени; мутация — это акт изменения одного из этих значений
на месте. Состояние без мутации возможно — снимок значений, которые никогда не
изменяются (только объявления const). Мутация без состояния труднее поддаётся
определению, но суть в том, что оба понятия взаимосвязаны, хотя и различны. Сохранять
точность этих понятий помогает рассуждать о природе ошибок.
Почему состояние порождает ошибки. Мощь изменяемого состояния — переменной,
отслеживающей накопленную историю, — одновременно является его опасностью. Если две
части программы могут изменять одну и ту же переменную, они могут мешать друг другу.
Одна часть устанавливает score = 0 для сброса игры; другая часть была в процессе
вычисления, ожидая, что score не равен нулю. Мутация, выполненная первой частью,
оставила вторую часть с неверным предположением.
Этот класс ошибок — когда одна мутация нарушает ожидания другой части программы — является причиной того, что значительная часть разработки программного обеспечения посвящена контролю мутаций: ограничению того, какие части программы могут изменять какие переменные и когда.
Трассировка состояния через несколько мутаций.
let score = 0;
let lives = 3;
let level = 1;Снимок состояния после строки 3: { score: 0, lives: 3, level: 1 }
score = score + 10; // игрок набрал очкиСнимок состояния после строки 4: { score: 10, lives: 3, level: 1 }
Мутация: score изменился на месте. lives и level не тронуты.
lives = lives - 1; // игрок погибСнимок состояния после строки 5: { score: 10, lives: 2, level: 1 }
Мутация: lives изменился на месте. score и level не тронуты.
level = level + 1; // следующий уровень
score = 0; // сброс счётаСнимок состояния после строки 7: { score: 0, lives: 2, level: 2 }
Две мутации подряд: level и score изменились по отдельности.
На каждом шаге «состояние» — это полный текущий снимок. «Мутация» — каждое отдельное изменение на месте.
let x = 5; let y = 10; x = x + y; Сколько всего записей в ячейки (мутаций) произошло за все три строки? Введи число.
let a = 1; let b = 2; a = b; b = 5; Каково значение a?
В программе есть переменные x=3, y=7, z=1. После 'x = y' каково состояние x?
let counter = 0; counter = counter + 1; counter = counter + 1; counter = counter + 1; Чему равен counter?
Верно или нет (1=верно, 0=нет): мутация изменяет значение ячейки на месте, не выделяя новую ячейку памяти.
Что такое состояние программы?
Состояние — полный снимок текущих значений всех живых переменных в один момент. Мутация — изменение на месте: присваивание, перезаписывающее ячейку по тому же адресу и уничтожающее предыдущее значение. Состояние — это накопленный результат всех предшествующих мутаций. Каждый раз, когда переменной присваивается новое значение, состояние меняется: содержимое ровно одной ячейки становится другим, остаток снимка не затронут. Программы с состоянием хранят и изменяют значения со временем; вычисления без состояния зависят только от своих входных данных. Понимание границы между ними — основа для рассуждений о поведении программ и природе ошибок.