Ruby on Rails regexp equals-tilde 与 array include 用于检查选项列表

标签 ruby regex ruby-on-rails-3

我正在使用 Rails 3.2.3 和 Ruby 1.9.3p0。

我发现我经常需要确定某个字符串是否出现在选项列表中。看来我可以使用 Ruby 数组 .include method :

<% if ['todo','pending','history'].include?(params[:category]) %>

或正则表达式 equals-tilde match shorthand用竖线分隔选项:

<% if params[:category] =~ /todo|pending|history/ %>

就性能而言,一个比另一个好吗?

还有更好的方法吗?

最佳答案

总结:Array#include? 包含 String 元素,在接受和拒绝输入时均胜出,对于您的示例只有三个可接受的值。对于要检查的更大的集合,看起来 Set#include?String 元素可能会获胜。


如何测试

我们应该根据经验对此进行测试。

您可能还需要考虑以下几个备选方案:预编译的正则表达式、符号列表和带有 String 元素的 Set

我认为性能还可能取决于您的大部分输入是否落入预期集合并被接受,或者大多数输入是否落在集合之外并被拒绝。

这是一个实证测试脚本:

require 'benchmark'
require 'set'

strings = ['todo','pending','history']
string_set = Set.new(strings)
symbols = strings.map(&:to_sym)
regex_compiled = Regexp.new(strings.join("|"))

strings_avg_size = (strings.map(&:size).inject {|sum, n| sum + n}.to_f / strings.size).to_i
num_inputs = 1_000_000

accepted_inputs = (0...num_inputs).map { strings[rand(strings.size)] } 
rejected_inputs = (0...num_inputs).map { (0..strings_avg_size).map { ('a'...'z').to_a[rand(26)] }.join }

Benchmark.bmbm(40) do |x|
  x.report("Array#include?, Strings, accepted:") { accepted_inputs.map {|s| strings.include?(s) } }
  x.report("Array#include?, Strings, rejected:") { rejected_inputs.map {|s| strings.include?(s) } }
  x.report("Array#include?, Symbols, accepted:") { accepted_inputs.map {|s| symbols.include?(s.to_sym) } }
  x.report("Array#include?, Symbols, rejected:") { rejected_inputs.map {|s| symbols.include?(s.to_sym) } }
  x.report("Set#include?, Strings, accepted:") { accepted_inputs.map {|s| string_set.include?(s) } }
  x.report("Set#include?, Strings, rejected:") { rejected_inputs.map {|s| string_set.include?(s) } }
  x.report("Regexp#match, interpreted, accepted:") { accepted_inputs.map {|s| s =~ /todo|pending|history/ } }
  x.report("Regexp#match, interpreted, rejected:") { rejected_inputs.map {|s| s =~ /todo|pending|history/ } }
  x.report("Regexp#match, compiled, accepted:") { accepted_inputs.map {|s| regex_compiled.match(s) } }
  x.report("Regexp#match, compiled, rejected:") { rejected_inputs.map {|s| regex_compiled.match(s) } }
end

结果

Rehearsal ---------------------------------------------------------------------------
Array#include?, Strings, accepted:        0.210000   0.000000   0.210000 (  0.215099)
Array#include?, Strings, rejected:        0.530000   0.010000   0.540000 (  0.543898)
Array#include?, Symbols, accepted:        0.330000   0.000000   0.330000 (  0.337767)
Array#include?, Symbols, rejected:        1.870000   0.050000   1.920000 (  1.923155)
Set#include?, Strings, accepted:          0.270000   0.000000   0.270000 (  0.274774)
Set#include?, Strings, rejected:          0.460000   0.000000   0.460000 (  0.463925)
Regexp#match, interpreted, accepted:      0.380000   0.000000   0.380000 (  0.382060)
Regexp#match, interpreted, rejected:      0.650000   0.000000   0.650000 (  0.660775)
Regexp#match, compiled, accepted:         1.130000   0.080000   1.210000 (  1.220970)
Regexp#match, compiled, rejected:         0.630000   0.000000   0.630000 (  0.640721)
------------------------------------------------------------------ total: 6.600000sec

                                              user     system      total        real
Array#include?, Strings, accepted:        0.210000   0.000000   0.210000 (  0.219060)
Array#include?, Strings, rejected:        0.430000   0.000000   0.430000 (  0.444911)
Array#include?, Symbols, accepted:        0.340000   0.000000   0.340000 (  0.341970)
Array#include?, Symbols, rejected:        1.080000   0.000000   1.080000 (  1.089961)
Set#include?, Strings, accepted:          0.270000   0.000000   0.270000 (  0.281270)
Set#include?, Strings, rejected:          0.400000   0.000000   0.400000 (  0.406181)
Regexp#match, interpreted, accepted:      0.370000   0.000000   0.370000 (  0.366931)
Regexp#match, interpreted, rejected:      0.560000   0.000000   0.560000 (  0.558652)
Regexp#match, compiled, accepted:         0.920000   0.000000   0.920000 (  0.915914)
Regexp#match, compiled, rejected:         0.620000   0.000000   0.620000 (  0.627620)

结论

(见上面的总结)

经过深思熟虑,符号数组对于被拒绝的输入会非常慢,因为在进行检查之前,这些随机字符串中的每一个都必须驻留在符号表中。

即使经过深思熟虑,编译后的 Regexp 的性能也会如此糟糕,这对我来说意义不大,尤其是与在代码中解释为文字的 Regexp 相比。谁能解释为什么它表现如此糟糕?

关于Ruby on Rails regexp equals-tilde 与 array include 用于检查选项列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11905925/

相关文章:

ruby - Ruby 中是否有等效的 addslashes?

ruby - float 域错误

ruby-on-rails - 显示Flash消息的最佳实践方法

ruby-on-rails - Active Admin Ruby on Rails仪表板 Controller 错误

ruby - 你如何在 Ruby/Tk 中制作箭头键事件处理程序

ruby - 安装 travis 命令行窗口

ruby-on-rails - 在 Ruby 中初始化具有高索引的空数组是否昂贵?

regex - 如何查找名称中包含换行符的文件

java - 如何使一个组可选,它捕获模式并捕获相同的组号?

ruby-on-rails - 为什么正则表达式不能匹配@符号?