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

Почему? Никто не знает.

Ну, я знаю.

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

Почему? Потому что вещи не то, чем кажутся. Цикл постоянно меняется.

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

var numbers = [10, 20, 30]
for (var i = 0; i < numbers.length; i++) {
  console.log(numbers[i]);
}

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

У нас также есть цикл с i в нем. Но что я? Это другое значение? Ну нет. Это заполнитель, который мы используем для запуска кода. Каждый раз, когда мы проходим цикл, мы используем i для поиска заданного значения в текущей позиции i.

Давайте сломаем это.

Наш цикл состоит из трех этапов, разделенных точкой с запятой.

  1. Сначала i устанавливается в ноль, этот этап известен как инициализатор, который в человеческом общении является отправной точкой, подобно искре, которая начинает цикл.

Ok, i is 0.

2. На втором этапе нашего цикла у нас есть условие, в данном случае наше условие говорит: «Если я меньше, чем длина списка предметов, которые я держу (в данном случае 3), то двигаться дальше».

3. Это подводит нас к третьему и последнему этапу. я был в поездке, она началась как 0, затем, поскольку она соответствовала требованиям нашего состояния, она двинулась дальше. Следующая инструкция в нашем цикле — i++, которая в разбивке соответствует i = i + 1. По сути, это даст i новое значение…

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

Это означает, что печатается элемент, находящийся в позиции 0. В нашем массиве numbers это число 10.

Теперь мы можем увеличить. Если i = 0 и мы добавляем к нему 1, то каково новое значение i?

Бинго, это 1.

Далее i = 1, соответствует ли это нашему условию? Да, потому что 1 меньше 3, поэтому мы переходим к части приращения нашего цикла…

О, нет, подожди. Сначала мы выполняем остальное, что находится в цикле. Итак, мы возвращаемся к нашему массиву и, используя текущее значение i (1), ищем значение в этой позиции. Это значение равно 20.

Теперь мы увеличиваем число, и i становится равным 2.

Сколько удовольствия я получил сегодня? я такой непредсказуемый.

Теперь я равен 2, это меньше длины нашего массива? Да! Итак, мой путь продолжается. Мы снова на стадии i++. Теперь мы найдем значение в позиции 2 в нашем массиве и сначала напечатаем то, что напечатает 30.

Отлично, теперь мы снова увеличиваем i, i = i + 1, что равно 3!

I = 3, на 3 меньше длины нашего массива? Нет, это не так. Итак, вот где мое путешествие заканчивается, потому что мы не можем удовлетворить требования условия нашего цикла.

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

В Ruby зацикливание немного приятнее.

Один механизм циклов в Ruby использует метод .each для массива. Мы даем массиву метод .each, что означает, что мы говорим ему выполнить следующий код для каждого элемента в массиве. Подобно циклу for в Javascript, мы используем заполнитель для доступа к каждому значению в массиве, но на этот раз, поскольку мы используем .each, положение значений не имеет значения, потому что в любом случае мы мы будем перебирать их, начиная с первого, который находится в позиции 0. Мы также даем каждому элементу массива имя, в нашем случае это friend. Затем мы используем do для обозначения начала нашего блока, за которым следуют некоторые инструкции и заканчиваем концом.

Для |friend|, также известного как каждый элемент нашего массива, примените puts. Или, проще говоря, мы говорим, что печатаем каждый элемент массива.

furry_friends = ['dog', 'cat', 'rabbit']
furry_friends.each do |friend|
  puts friend
end

Ждать? Какие? Ведь нельзя же так просто?

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

У меня есть три элемента, первый — собака, поэтому я поставлю собаку.

Второй — кошка, поэтому я напечатаю кошку.

Третий — кролик, так что, думаю, я тоже напечатаю кролика.

Элементов больше нет? Хорошо, тогда я закончил.

Это так просто.

У нас есть два совершенно разных способа перебора массива.

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

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