В каждом языке программирования имеются операторы цикла, цель которых, выполнение повторяющихся участков кода. Циклы бывают конечные и бесконечные.
Цикл for
относится к конечным циклам, то есть мы заранее знаем условие, при достижении которого цикл прекратит свою работу.
В С++ цикл for
используется для создания цикла со счетчиком:
for (int i = 0; i < 5; i++) {
cout << i << endl;
}
Цикл for
в Python:
for number in [0, 1, 2, 3, 4]:
print(number)
Цикл for
в JavaScript:
let step;
for (step = 0; step < 5; step++) {
console.log(step);
}
В руби оператор for
тоже есть. Синтаксис у него очень простой и чем-то похожий на питон:
for <элемент> in <массив> [do]
<действие>
end
Такой цикл выполняет <действие> один раз для каждого <элемента> в <массиве>. Ключевое слово [do]
一 здесь можно (и нужно) опустить.
Допустим, мы хотим вывести все футбольные команды из такого массива:
["ЦСКА", "Зенит", "Динамо", "Спартак"]
С помощью for
это можно сделать так:
teams = ["ЦСКА", "Зенит", "Динамо", "Спартак"]
for team in teams
puts team
end
Такой код выведет:
ЦСКА
Зенит
Динамо
Спартак
Конечно, в массиве могут быть любые объекты, не только строки. Например, выведем степени числа 2 с помощью for
и массива чисел:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in numbers do
puts 2**i
end
При выводе будет:
2
4
8
16
32
64
128
256
512
1024
С помощью for
в руби можно обойти не только массив, но и, например, диапазон (объект класса Range
). Перепишем наш пример про степени двойки:
for i in (1..10)
puts 2**i
end
Результат будет такой же:
2
4
8
16
32
64
128
256
512
1024
В руби вместо цикла for
для диапазонов (и массивов) принято использовать метод each
. Делает он абсолютно то же самое, но записывается немного иначе.
Пример со степенями числа 2 перепишем через each
:
(1..10).each do |i|
puts 2**i
end
Или даже короче, в одну строчку:
(1..10).each { |i| puts 2**i }
Вывод (в обоих случаях):
2
4
8
16
32
64
128
256
512
1024
Итератор each
перебирает элементы диапазона (1..10)
, и передает элемент i
в блок, где каждый i
используется в качестве степени двойки (для оператора **
), которую мы потом выводим на экран методом puts
.
На русский язык этот код, записанный на ruby
, можно перевести как-то так: «возьми каждый
элемент из диапазона (1..10)
, возведи число 2 в эту степень и выведи на экран».
Выглядит более наглядно и емко. При чтении кода сразу видно, что происходит. Без лишних слов, как в случае с for
. Поэтому в руби предпочитают использовать each
.
Различие между циклами с for
и с each
— в областях видимости.
Любая переменная, которую вы объявляете в цикле for
, будет доступна вне цикла, переменные в блоке итератора each
— доступны только внутри этого блока.
arr = [1, 2, 3]
for element in array do
puts element
end
# element доступен за пределами цикла
element #=> 3
array.each { |element| puts elements }
# element недоступен за пределами блока `each`
element #=> NameError: undefined local variable or method `element`
Вы можете осуществить перебор диапазона (как в цикле с each
, так и в цикле с for
) только в том случае, если все элементы диапазона поддерживают метод succ
(следующий).
('*'..'+').each { |x| puts "#{x} -> #{x.succ}" }
puts
('d'..'f').each { |x| puts "#{x} -> #{x.succ}" }
Выведет:
* -> +
+ -> ,
d -> e
e -> f
f -> g
Перебор элементов из интервала, состоящего из экземпляров класса Float
невозможен.
(0.1..0.4).each { |x| puts x }
#=> in `each`: can't iterate from Float (TypeError)
* — для любознательных
На самом деле, внутри оператора for
используется всё тот же each
. Об этом написано, например, в документации:
The for loop consists of for followed by a variable to contain the iteration argument followed by in and the value to iterate over using each.
Также можно почитать об этом здесь. Автор считает, что for
был сделан в руби только для того, чтобы разработчикам, которые пришли в руби из других языков, было комфортнее.
Опытные разработчики в руби не пользуются for
. Используйте вместо него each
.