Прежде всего, вот документация для grep
Позвольте мне очистить ваш код и объяснить его по частям
# 1
cars = [:Ford, :Toyota, :Audi, :Honda]
# 2
ucased_cars = cars.collect do |c|
c.to_s
end.grep(/^Ford/) do |car| # 3
puts car.upcase # 4
car.upcase # 5
end
# 6
# 7
puts "ucased:" + ucased_cars.to_s
Объявить массив символов
Преобразование символов в строки с помощью метода collect. Вы получаете ["Ford", "Toyota", "Audi", "Honda"]
Подайте этот массив строк в grep. Любой из предметов, которые соответствуют регулярному выражению /^Ford/
, попадет в блок
Блок распечатывает строку в верхнем регистре, которую он получил
Блок возвращает строку в верхнем регистре, которую grep принимает в качестве «значения совпадения»
возвращаемое значение из grep (который является массивом всех "значений соответствия") присваивается ucased_cars
, это ["FORD"]
, потому что это было единственное, что соответствовало регулярному выражению.
Затем печатается. выполнение to_s
для массива просто печатает все элементы, застрявшие вместе, как это. Это не очень полезно, вам лучше печатать ucased_cars.inspect
Чтобы ответить на ваш вопрос о том, как работает grep за кулисами ...
На приведенной выше странице документации показан исходный код C для самого grep. Это в основном делает это:
- выделить новый массив ruby (динамический размер)
- вызовите
rb_iterate
, чтобы пройтись по каждому элементу в источнике, передав некоторый специфичный для grep код.
rb_iterate
также используется collect
, each_with_index
и кучей других вещей.
Поскольку мы знаем, как работают команды collect / each / etc, нам не нужно больше разбираться в исходном коде, у нас есть наш ответ, и это ваш [B].
Чтобы объяснить более подробно, он делает это:
- Создайте новый массив для хранения возвращаемых значений.
- Получить следующий предмет из источника
- Если это соответствует регулярному выражению:
- Если был задан блок, вызовите блок и, что бы ни возвращал блок, поместите его в возвращаемые значения.
- Если блок не был задан, поместите элемент в возвращаемые значения
- Перейти к 2, повторять до тех пор, пока в источнике больше не останется элементов.
Что касается вашего комментария "А, кажется, имеет больше смысла" - я не согласен.
Идея состоит в том, что блок что-то делает с каждым элементом. Если он сначала отсканирует источник, а затем передаст массив совпадений в блок, ваш блок должен будет сам вызвать each
, что будет громоздко.
Во-вторых, это было бы менее эффективно. Что происходит, например, если ваш блок вызывает return
или выдает ошибку? В его текущем воплощении вы избегаете необходимости сканировать остальную часть источника. Если бы он уже сканировал весь список источников заранее, вы бы потратили впустую все эти усилия.