Как блочная форма Array#new работает с "Array.new(10) { |e| e = e * 2 }"?
У меня проблемы с пониманием роли внутри фигурных скобок.
Array.new(10) { |e| e = e * 2 }
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Я получаю, что создается новый массив с десятью значениями, но что делает вторая половина?
2 ответа
Давайте рассмотрим это в деталях:
nums = Array.new(10)
Это создает новый массив с 10 элементами. Для каждого элемента массива он передает управление блоку, указанному:
{ |e| e = e * 2 }
|e|
представляет индекс элемента. Индекс - это позиция в массиве. Это начинается в 0 и заканчивается в 9, так как массив имеет 10 элементов. Вторая часть умножает индекс на 2 и возвращает значение. Это потому что e * 2
, будучи последним оператором в блоке, возвращается. Возвращенное значение затем применяется к значению этого элемента. Таким образом, мы получаем следующий массив:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
РЕДАКТИРОВАТЬ
Как упомянуто pjs и чтобы избежать проблем в будущем, более простой способ написать тот же код:
Array.new(10) { |e| e * 2 }
Что делает ваш код
nums = Array.new(10) { |e| e = e * 2 }
Разбейте его шаг за шагом в IRB или Pry, чтобы увидеть, что на самом деле делает ваш код. Например:
Array.new(10)
создает массив из 10 элементов.Array.new(10) { |e| puts e }
печатает индекс каждого элемента, который будет 0..9.- Конструкция
{ |e| e = e * 2 }
или более идиоматически{ |e| e * 2 }
, является блоком Ruby, который умножает индекс каждого элемента, переданного в блочную переменную e, на 2. nums = Array.new(10) { |e| e = e * 2 }
берет результат блока и сохраняет его в переменной nums.
Обратите внимание, что этот сохраненный массив создается через блочную форму Array # new, которая говорит, что при использовании блочной формы конструктора:
[A] n массив заданного размера создан. Каждый элемент в этом массиве создается путем передачи индекса элемента в данный блок и сохранения возвращаемого значения.
Пример в оригинальном посте, безусловно, является допустимым кодом Ruby, но не особенно понятен. Тот же самый результат может быть получен с более явным кодом, таким как пример ниже.
Одна из многих более четких альтернатив
Всегда есть несколько способов сделать что-то, особенно в Ruby. Если вы хотите быть более ясным о том, что делает ваш код, используйте менее "магическую" конструкцию. Например, ваш код примерно эквивалентен:
num_array = 0.upto(9).map { |int| int * 2 }
#=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]