Я понимаю последовательность событий в этом примере Ruby grep? - PullRequest
1 голос
/ 22 марта 2009

Я пытаюсь понять, как работает grep в этом примере. Код работает, но я не уверен на 100%, в какой последовательности происходят события или правильно ли я понимаю, что и когда возвращается.

cars = [:Ford, :Toyota, :Audi, :Honda]
ucased_cars = cars.collect do |c| 
c.to_s 
end
.grep(/^Ford/) do |car| 
puts car.upcase 
car.upcase 
end
puts "ucased:" + ucased_cars.to_s

Я думаю, что происходит:

  1. Я определяю массив символов
  2. Я вызываю метод collect с блоком, который заставляет каждый элемент Symbol, c, из массива cars конвертироваться в String внутри блока.
  3. collect возвращает массив строк
  4. grep вызывается для массива строк, возвращаемых функцией collect, и grep вызывает свой собственный блок для каждого элемента массива, car, в соответствии с шаблоном поиска, в результате чего элемент выводится в верхнем регистре и возвращается как часть массива. *
  5. grep возвращает массив строк в верхнем регистре, присваивая его 'ucased_cars'
  6. Массив ucased_cars должен быть преобразован в строку перед печатью.

Что касается шага # 4, то что из следующего лучше всего описывает работу grep:

[A] grep находит все строки, соответствующие шаблону. grep вызывает блок в этом массиве совпадений. grep возвращает результаты блока вызывающей функции.

[B] grep находит первую строку, соответствующую шаблону. grep вызывает блок в этом матче. возвращаемое значение этого блока где-то временно накапливается. grep ищет следующий элемент массива. если это совпадает, grep вызывает блок в этом совпадении. grep добавляет возвращаемое значение этого блока во временное «хранилище» возвращаемых значений. grep просматривает следующий элемент массива, пока не находит больше совпадений. затем grep передает сложенные возвращаемые значения обратно в вызывающую функцию.

Мой вывод:

[A] кажется, имеет больше смысла.

[B] кажется ненужным обманом и не кажется эффективным или вероятным.

1 Ответ

12 голосов
/ 22 марта 2009

Прежде всего, вот документация для 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
  1. Объявить массив символов

  2. Преобразование символов в строки с помощью метода collect. Вы получаете ["Ford", "Toyota", "Audi", "Honda"]

  3. Подайте этот массив строк в grep. Любой из предметов, которые соответствуют регулярному выражению /^Ford/, попадет в блок

  4. Блок распечатывает строку в верхнем регистре, которую он получил

  5. Блок возвращает строку в верхнем регистре, которую grep принимает в качестве «значения совпадения»

  6. возвращаемое значение из grep (который является массивом всех "значений соответствия") присваивается ucased_cars, это ["FORD"], потому что это было единственное, что соответствовало регулярному выражению.

  7. Затем печатается. выполнение to_s для массива просто печатает все элементы, застрявшие вместе, как это. Это не очень полезно, вам лучше печатать ucased_cars.inspect

Чтобы ответить на ваш вопрос о том, как работает grep за кулисами ...

На приведенной выше странице документации показан исходный код C для самого grep. Это в основном делает это:

  • выделить новый массив ruby ​​(динамический размер)
  • вызовите rb_iterate, чтобы пройтись по каждому элементу в источнике, передав некоторый специфичный для grep код.
  • rb_iterate также используется collect, each_with_index и кучей других вещей.

Поскольку мы знаем, как работают команды collect / each / etc, нам не нужно больше разбираться в исходном коде, у нас есть наш ответ, и это ваш [B].

Чтобы объяснить более подробно, он делает это:

  1. Создайте новый массив для хранения возвращаемых значений.
  2. Получить следующий предмет из источника
  3. Если это соответствует регулярному выражению:
    • Если был задан блок, вызовите блок и, что бы ни возвращал блок, поместите его в возвращаемые значения.
    • Если блок не был задан, поместите элемент в возвращаемые значения
  4. Перейти к 2, повторять до тех пор, пока в источнике больше не останется элементов.

Что касается вашего комментария "А, кажется, имеет больше смысла" - я не согласен.

Идея состоит в том, что блок что-то делает с каждым элементом. Если он сначала отсканирует источник, а затем передаст массив совпадений в блок, ваш блок должен будет сам вызвать each, что будет громоздко.

Во-вторых, это было бы менее эффективно. Что происходит, например, если ваш блок вызывает return или выдает ошибку? В его текущем воплощении вы избегаете необходимости сканировать остальную часть источника. Если бы он уже сканировал весь список источников заранее, вы бы потратили впустую все эти усилия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...