Про массивы я уже писал в предыдущих выпусках. Сама концепция массивов довольно проста, и вероятно вы её поняли уже на интуитивном уровне, но в этом выпуске я объясню более подробно, когда нужны массивы и как с ними работать.
Более сложные понятия в программировании изобретаются тогда, когда перестает хватать простых. Поэтому давайте начнем с простых и посмотрим, когда их перестанет хватать.
Попробуем написать самую простую игру: игрок бьет монстра, а монстр бьет игрока.
Как выглядит игрок или монстр, где они находятся, как именно бьют – не интересует вообще. Их даже на экране не будет. Есть ровно одно действие: отнять здоровье.
Чтобы сохранить в памяти состояние игрока, требуется всего одно число – это его здоровье. То же самое требуется для монстра. Заведем две переменные:
var player_hp = 100; // это здоровье игрока
var monster_hp = 100; // это здоровье монстра
Теперь, когда нужно отнять здоровье игрока, мы будем обращаться к переменной player_hp, а когда нужно отнять здоровье монстра – к переменной monster_hp.
Но что, если монстров 5 штук, а игрок обладает ядерным оружием, чтобы нанести удар по всем монстрам одновременно?
Во-первых, придется завести здоровье каждого монстра:
var monster1_hp = 100;
var monster2_hp = 100;
var monster3_hp = 100;
var monster4_hp = 100;
var monster5_hp = 100;
А при нанесении удара нужно будет отнять здоровье у каждого монстра:
monster1_hp--; // это значит "уменьшить на 1"
monster2_hp--;
monster3_hp--;
monster4_hp--;
monster5_hp--;
Всё хорошо, но какие здесь недостатки?
Во-первых, если монстров 1000, это что, нам надо завести тысячу переменных monster1_hp, monster2_hp, ... monster1000_hp?
Во-вторых, чтобы провести операцию со всеми монстрами, надо к каждому обратиться поименно.
А что, если часть монстров умерла? Каждый раз проверять, умер уже монстр или нет? И копипастить этот код 1000 раз?
Собственно всё, нам уже перестало хватать простых понятий. То, что можно запрограммировать для 1 монстра, уже совсем не работает даже для 10.
Тут и возникает идея. Зачем всех монстров знать по имени? Они все одинаковые. Не нужно заводить переменную на каждого. Нужно просто сложить их в память подряд и вызывать по номеру.
Это приводит нас к концепции массива. Вот массив, который содержит 5 монстров, у каждого монстра здоровье 100:
var monsters_hp = [100, 100, 100, 100, 100];
В памяти он представлен как цепочка из 5 ячеек с записанными в них числами 100.
В переменной monsters_hp хранится адрес начала этой цепочки. Как получить доступ к 2-му или 5-му монстру? Все элементы массива имеют адрес внутри массива. Я уже писал, что адреса отсчитываются от начала памяти и поэтому начинаются с 0. Для элемента, который содержится в массиве, началом памяти является начало массива, и адрес отсчитывается от него.
Первый элемент в массиве будет иметь адрес 0, второй элемент адрес 1, и так далее. Записывается это так:
monsters_hp[0] - это элемент массива с адресом 0 (первый)
monsters_hp[2] - это элемент массива с адресом 2
monsters_hp[4] - это элемент массива с адресом 4 (последний)
Имея переменную, которая хранит адрес начала массива в памяти, и приписывая к этой переменной смещение от начала, мы можем получить доступ к любому элементу из массива.
Если вы попробуете обратиться к несуществующему адресу внутри массива, трансляторы разных языков будут вести себя по-разному. Результат зависит от философии языка. Кто-то выдаст фатальную ошибку, кто-то молча добавит в массив еще один элемент, а кому-то ПЛЕВАТЬ – если тебе нужен адрес относительно чего угодно, то бери его, а понятие массива лишь умозрительно.
Ладно, имея массив с монстрами, как теперь отнять у них здоровье? Необходимо перебрать адреса массива и отнять здоровье у каждого монстра:
monsters_hp[0]--;
monsters_hp[1]--;
monsters_hp[2]--;
monsters_hp[3]--;
monsters_hp[4]--;
Но стоп. Мы же по факту ничего не добились. Мы только избавились от того, чтобы назначать каждому монстру по отдельной переменной. Но чтобы снять у них здоровье, нам по-прежнему нужно повторять код для каждого монстра. А если их будет всё-таки 1000?
Получается, нам в очередной раз перестало хватать простых понятий. И тут нам на помощь придут циклы, о которых – в следующем выпуске.