В моем текущем проекте peacock (маленькая жемчужина для управления файлами .gitignore через командную строку) мне пришлось провести множество строковых тестов специально для первой и последней буквы. Вначале я использовал регулярные выражения, хотя знал, что в Ruby есть несколько встроенных методов для проверки этого особого случая начальных и конечных букв под названием start_with? и end_with?

Я немного погуглил, но не нашел теста, сравнивающего эти методы и регулярное выражение, поэтому решил позаботиться об этом. Код очень прост:

require ‘benchmark’
n = 1000000
string = “a string”
REGEX = /\Aa/
Benchmark.bm(13) do |x|
 x.report(“start_with: “) { n.times do; string.start_with?(“a”); end }
 x.report(“regex: “) { n.times do; string =~ REGEX; end }
end

Результат на моем компьютере был:

                user   system    total        real
start_with: 0.100000 0.000000 0.100000 ( 0.097530)
regex:      0.240000 0.000000 0.240000 ( 0.243886)

Используя метод start_with? более чем в два раза быстрее, чем при использовании регулярных выражений.

То же самое при сравнении end_with?

require 'benchmark'
n = 1000000
string = “a string”
REGEX = /g\z/
Benchmark.bm(13) do |x|
 x.report(“end_with: “) { n.times do; string.end_with?(“g”); end }
 x.report(“regex: “) { n.times do; string =~ REGEX; end }
end

Результаты для:

              user   system    total        real
end_with: 0.110000 0.000000 0.110000 ( 0.106032)
regex:    0.250000 0.000000 0.250000 ( 0.257426)

Так как же start_with? и end_with? на самом деле реализовано?

Чтобы ответить на этот вопрос, нам нужно немного покопаться в исходном коде Ruby (точнее, в файле string.c). Там находим следующую функцию:

rb_str_start_with(int argc, VALUE *argv, VALUE str) 
{ 
  int i; 
  for (i=0; i<argc; i++) { 
    VALUE tmp = argv[i]; 
    StringValue(tmp); 
    rb_enc_check(str, tmp); 
    if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue; 
    if (memcmp(RSTRING_PTR(str), RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0) 
      return Qtrue; 
  } 
  return Qfalse; 
}

Нам не нужно знать, что именно делает каждая строка, чтобы понять, как она работает.

  • argv содержит все строки, переданные методу в виде массива строк.
  • str содержит строку, начальные символы которой необходимо проверить.
  • функция выполняет итерацию по argv и сравнивает str и argv [i], используя длину argv [i] в качестве размера символов, которые она хочет сравнивать

Таким образом, это намного быстрее, чем использование регулярного выражения.

TL; DR: используйте start_with? и end_with? вместо регулярных выражений