В моем текущем проекте 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? вместо регулярных выражений