Ruby on rails 从字符串中删除单词时,正则表达式会比遍历单词列表更快吗?
现在,我正在使用此技术从搜索中删除不需要的单词:Ruby on rails 从字符串中删除单词时,正则表达式会比遍历单词列表更快吗?,ruby-on-rails,ruby,regex,string,Ruby On Rails,Ruby,Regex,String,现在,我正在使用此技术从搜索中删除不需要的单词: IGNORED_WORDS.each { |e| params[:query].gsub!(e,'') } 我的同事告诉我,正则表达式的替换会更快。这是真的吗?如果是,为什么?看起来天真的正则表达式并没有更快: require "benchmark" IGNORED_WORDS = ["foo", "bar"] def regex my_string = "I foo'd her right in the bar last night!
IGNORED_WORDS.each { |e| params[:query].gsub!(e,'') }
我的同事告诉我,正则表达式的替换会更快。这是真的吗?如果是,为什么?看起来天真的正则表达式并没有更快:
require "benchmark"
IGNORED_WORDS = ["foo", "bar"]
def regex
my_string = "I foo'd her right in the bar last night!"
IGNORED_WORDS.each { |e| my_string.gsub! Regexp.new(Regexp.escape(e)), '' }
end
def non_regex
my_string = "I foo'd her right in the bar last night!"
IGNORED_WORDS.each { |e| my_string.gsub!(e, '') }
end
puts "With regex: ", Benchmark.measure { (1..100000).each { regex } }
# 1.750000 0.010000 1.760000 ( 1.780110)
puts "Without regex: ", Benchmark.measure { (1..100000).each { non_regex } }
# 1.580000 0.000000 1.580000 ( 1.592312)
在这种情况下,从被忽略单词列表中的每个单词创建Regex
实例会产生额外的开销,而使用gsub
只需要原始字符串
但是,如果您知道所有被忽略的单词都有一个可以编写自定义正则表达式的模式,那么就可以避免在
被忽略的单词
数组上循环-这肯定会更快。(O(1)
而不是O(n)
,其中n
精确地说是被忽略的单词数。如果您使用正则表达式,您将使用Regexp.union
将所有被忽略的单词组合到一个正则表达式中,我想到了两个原因,为什么使用它可能更快
1) Ruby中不需要迭代。Regex-match必须检查单个Regex中的替代项,但这是由用C实现的Oniguruma Regex引擎完成的。这比用解决方案在Ruby中迭代每个被忽略的单词要快
2) 由于您将使用一个包含所有单词的正则表达式作为替代项,因此在某个特定位置的匹配在使用某个替代项成功后停止,而忽略单词列表中的其他替代项将不会在该位置进行匹配
2)意味着您可以通过按文本中出现的可能性顺序重新排列被忽略的单词来提高速度
编辑对不起,2)可能没有太多意义,因为在您的其他方法中,gsub!将从原始字符串中删除匹配的单词,因此位于该位置的子字符串将不会与后面的单词匹配。在这方面,应该没有区别。但是,如果您在不替换的情况下进行匹配,例如计算被忽略的单词等,那么regex方法在这方面也会更快。正如@sawa刚才所说,查找任何被忽略的单词(如果数组较大)的更快方法是执行以下操作:
# just compile the regexp once, then use it as many times as you want
IGNORED = Regexp.union(*IGNORED_WORDS)
# then inside a method somewhere...
string.gsub!(IGNORED, '')
我不知道Oniguruma regexp引擎有多聪明,但是一个好的基于DFA的引擎会将这样一个regexp编译成一个状态机,它会对字符串进行单次线性传递(从不回溯)。如果它将regexp直接编译为本机代码,那就更好了(但Oniguruma没有——它在内部使用字节码)。对于这样的任务,基于DFA的引擎会变得很糟糕——没有显式的字符串操作调用会触及它。取决于具体情况。只要测试一下。如果你像这样使用regexp,它自然会很慢。如果忽略的单词很大,有一种方法会快得多。。。让我写一个答案。