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

1 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
Run Code Online (Sandbox Code Playgroud)

我认为发生的是:

  1. 我定义了一个符号数组
  2. 我用一个块调用collect方法,这个块导致cars数组的每个Symbol元素c被转换成块内的String.
  3. collect返回一个字符串数组
  4. grep在由collect返回的字符串数组上调用,grep在每个数组元素car上调用它自己的块,匹配搜索模式,导致元素被打印,大写并作为数组的一部分返回.
  5. grep返回一个大写字符串数组,将其分配给'ucased_cars'
  6. 数组ucased_cars必须在打印前转换为String.

就第4步而言,以下哪项最能说明grep的工作原理:

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

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

我的结论:

[A]似乎更有意义.

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

Ori*_*rds 12

首先,这是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
Run Code Online (Sandbox Code Playgroud)
  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

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

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

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

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

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

  1. 创建一个新数组来保存返回值.
  2. 从源获取下一个项目
  3. 如果它匹配正则表达式:
    • 如果给出了一个块,则调用该块,无论块返回什么,都将其放入返回值中.
    • 如果未给出块,则将该项放入返回值中
  4. 转到2,重复直到源中没有更多项目.

至于你对"A似乎更有意义"的评论 - 我不同意.

这个想法是块对每个元素做了一些事情.如果它首先扫描了源,然后将匹配数组传递给块,那么你的块就必须自己调用each,这很麻烦.

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