我是否理解了这个Ruby grep示例中的事件序列?

时间:2009-03-22 19:41:44

标签: ruby grep

我试图了解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方法,该块使cars数组的每个Symbol元素c转换为块内的String。
  3. collect返回一个字符串数组
  4. 在collect返回的字符串数组上调用grep,grep在每个数组元素car上调用它自己的块,匹配搜索模式,导致元素被打印,大写并作为数组的一部分返回。
  5. grep返回一个大写字符串数组,将其分配给'ucased_cars'
  6. 数组ucased_cars必须在打印前转换为String。
  7. 就第4步而言,以下哪项最能说明grep的工作原理:

    [A] grep查找与模式匹配的所有字符串。 grep在这个匹配数组上调用块。 grep将块的结果返回给调用函数。

    [B] grep找到匹配模式的第一个字符串。 grep在这场比赛中调用该块。这个区块的返回值暂时堆积在某个地方。 grep搜索数组的下一个元素。如果匹配,grep会在此匹配上调用该块。 grep将此块的返回值添加到返回值的临时“存储”中。 grep查看下一个数组元素,直到找不到更多匹配项。然后grep将堆积的返回值传递回调用函数。

    我的结论:

    [A]似乎更有意义。

    [B]似乎有很多不必要的捏造,似乎没有效率或可能。

1 个答案:

答案 0 :(得分:12)

首先,here's the documentation for 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. 该块返回upcased字符串,grep然后作为“匹配值”

  6. 来自grep的返回值(这是所有“匹配值”的数组)被分配给ucased_cars,它是["FORD"],因为那是唯一与之匹配的东西正则表达式。

  7. 然后打印出来。在数组上执行to_s只会打印所有阻塞的元素。这不是很有用,最好打印ucased_cars.inspect

  8. 回答关于grep如何在幕后工作的问题......

    上面的文档页面显示了grep本身的C源代码。它基本上是这样做的:

    • 分配一个新的ruby数组(动态大小)
    • 调用rb_iterate遍历源代码中的每个元素,并传入一些特定于grep的代码。
    • rb_iterate也被collecteach_with_index和其他一些东西使用。

    由于我们知道collect / each / etc是如何工作的,我们不需要在源代码中进行任何更多的探索,我们有答案,而且它是你的[B]。

    为了更详细地解释,它是这样做的:

    1. 创建一个新数组以保存返回值。
    2. 从源
    3. 获取下一个项目
    4. 如果它与正则表达式匹配:
      • 如果给出了一个块,则调用该块,无论块返回什么,都将它放在返回值中。
      • 如果未给出阻止,请将该项目放入返回值
    5. 转到2,重复直到源中没有其他项目。
    6. 至于你对“A似乎更有意义”的评论 - 我不同意。

      这个想法是该块使用每个元素执行某些操作。如果它首先扫描了源,然后将匹配数组传递给块,那么你的块就必须自己调用each,这很麻烦。

      其次,效率会降低。例如,如果您的块调用return或引发错误,会发生什么?在它的当前版本中,您可以避免扫描其余的源。如果它已经预先扫描了整个源列表,那么你就浪费了所有这些努力。