Как блочная форма 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, чтобы увидеть, что на самом деле делает ваш код. Например:

  1. Array.new(10) создает массив из 10 элементов.
  2. Array.new(10) { |e| puts e } печатает индекс каждого элемента, который будет 0..9.
  3. Конструкция { |e| e = e * 2 }или более идиоматически { |e| e * 2 }, является блоком Ruby, который умножает индекс каждого элемента, переданного в блочную переменную e, на 2.
  4. 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]
Другие вопросы по тегам