В рамках моих историй на этой неделе меня попросили написать технический пост. Один из элементов программирования, который я считаю наиболее сложным, — циклы.
Почему? Никто не знает.
Ну, я знаю.
Честно говоря, я думаю, что это потому, что циклы довольно хитры. Они звучат достаточно просто: у меня есть список вещей, я собираюсь пройтись по списку и выполнить действие над каждой вещью. Однако все не так просто.
Почему? Потому что вещи не то, чем кажутся. Цикл постоянно меняется.
Когда я впервые начал изучать программирование, я начал изучать 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.
Давайте сломаем это.
Наш цикл состоит из трех этапов, разделенных точкой с запятой.
- Сначала 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 также велика.
В любом случае, зацикливание сложно. В этом маленьком цикле происходит многое.