我试图理解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
我认为发生的是:
我定义了一个符号数组
我用一个块调用collect方法,这个块导致cars数组的每个Symbol元素c被转换成块内的String.
collect返回一个字符串数组
grep在由collect返回的字符串数组上调用,grep在每个数组元素car上调用它自己的块,匹配搜索模式,导致元素被打印,大写并作为数组的一部分返回.
grep返回一个大写字符串数组,将其分配给'ucased_cars'
数组ucased_cars必须在打印前转换为String.
就第4步而言,以下哪项最能说明grep的工作原理:
[A] grep查找与模式匹配的所有字符串.grep在这个匹配数组上调用块.grep将块的结果返回给调用函数.
[B] grep找到匹配模式的第一个字符串.grep在这场比赛中调用该块.这个区块的返回值暂时堆积在某个地方.grep搜索数组的下一个元素.如果匹配,grep会在此匹配上调用该块.grep将此块的返回值添加到返回值的临时"存储"中.grep查看下一个数组元素,直到找不到更多匹配项.然后grep将堆积的返回值传递回调用函数.
我的结论:
[A]似乎更有意义.
[B]似乎有很多不必要的捏造,似乎没有效率或可能性.
首先,这是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/
将被送入块中
该块打印出它所提供的上行字符串
该块返回upcased字符串,grep然后作为"匹配值"
从grep的返回值(这是所有的"匹配值"的数组)被分配到ucased_cars
,这是["FORD"]
,因为这是匹配正则表达式的唯一事情.
然后打印出来.在to_s
阵列上做一个只打印所有阻塞的元素.这不是很有用,你最好打印ucased_cars.inspect
回答你关于grep如何在幕后工作的问题......
上面的文档页面显示了grep本身的C源代码.它基本上是这样的:
分配一个新的ruby数组(动态大小)
调用rb_iterate
遍历源代码中的每个元素,并传入一些特定于grep的代码.
rb_iterate
也使用collect
,each_with_index
和一堆其他的东西.
由于我们知道collect/each/etc是如何工作的,我们不需要在源代码中进行任何更多的探索,我们有答案,而且它是你的[B].
为了更详细地解释,它这样做:
创建一个新数组来保存返回值.
从源获取下一个项目
如果它匹配正则表达式:
如果给出了一个块,则调用该块,无论块返回什么,都将其放入返回值中.
如果未给出块,则将该项放入返回值中
转到2,重复直到源中没有更多项目.
至于你对"A似乎更有意义"的评论 - 我不同意.
这个想法是块对每个元素做了一些事情.如果它首先扫描了源,然后将匹配数组传递给块,那么你的块就必须自己调用each
,这很麻烦.
其次,效率会降低.例如,如果你的块调用return
或引发错误会发生什么?在它的当前版本中,您可以避免扫描其余的源.如果它已经预先扫描了整个源列表,那么你就浪费了所有这些努力.