Есть предложения по улучшению (оптимизации) существующей подстановки строк в коде Perl?

#regex #perl #string #substitution

#регулярное выражение #perl #строка #подстановка

Вопрос:

Perl 5.8

Улучшения для довольно простых подстановок строк в существующем скрипте Perl.
Цель кода ясна, и код работает.

Для данной строки замените каждое вхождение символа табуляции, LF или CR одним пробелом и замените каждое вхождение двойной кавычки двумя двойными кавычками. Вот фрагмент из существующего кода:


 # replace all tab, newline and return characters with single space
$val01  =~s/[tnr]/ /g;
$val02  =~s/[tnr]/ /g;
$val03  =~s/[tnr]/ /g;

# escape all double quote characters by replacing with two double quotes
$val01  =~s/"/""/g;
$val02  =~s/"/""/g;
$val03  =~s/"/""/g;
  

Вопрос:Есть ли лучший способ выполнить эти манипуляции со строками?

Под «лучшим способом» я подразумеваю выполнять их более эффективно, избегая использования регулярных выражений (возможно, с помощью tr/// замены символов табуляции, новой строки и lf) или, возможно, используя using ( qr// ), чтобы избежать перекомпиляции.

ПРИМЕЧАНИЕ: Я рассматривал возможность переноса операций со строками в подпрограмму, чтобы уменьшить повторяемость регулярных выражений.

ПРИМЕЧАНИЕ: Этот код работает, он на самом деле не сломан. Я просто хочу знать, существует ли более подходящее соглашение о кодировании.

ПРИМЕЧАНИЕ: Эти операции выполняются в цикле, состоящем из большого числа (> 10000) итераций.

ПРИМЕЧАНИЕ: В настоящее время этот скрипт выполняется под perl v5.8.8. (В скрипте есть require 5.6.0 , но это можно изменить на require 5.8.8 . (Установка более поздней версии Perl в настоящее время не предусмотрена на рабочем сервере.)

 
    > perl -v
    This is perl, v5.8.8 built for sun4-solaris-thread-multi
    (with 33 registered patches, see perl -V for more detail)
  

Ответ №1:

На мой взгляд, ваше существующее решение выглядит нормально.

Что касается избежания перекомпиляции, вам не нужно беспокоиться об этом. Регулярные выражения Perl компилируются только один раз, как есть, если только они не содержат интерполированных выражений, чего нет в вашем.

Для полноты картины я должен упомянуть, что даже если присутствуют интерполированные выражения, вы можете указать Perl скомпилировать регулярное выражение только один раз, указав /o флаг.

 $var =~ s/foo/bar/;    # compiles once
$var =~ s/$foo/bar/;   # compiles each time
$var =~ s/$foo/bar/o;  # compiles once, using the value $foo has
                       # the first time the expression is evaluated
  

Комментарии:

1. 1 Ваши краткие примеры — это то, что мне было нужно, теперь документация perlop имеет смысл. Спасибо!

Ответ №2:

TMTOWTDI

Вы могли бы использовать функции tr, или index, или substr, или split в качестве альтернатив. Но вы должны провести измерения, чтобы определить наилучший метод для вашей конкретной системы.

Комментарии:

1. Я могу проводить измерения, но было бы наиболее полезно, если бы у меня был альтернативный код для фактического проведения измерений. Как можно «измерить» читаемость кода? Что касается Тима Тоуди, я считаю, что это один из существенных недостатков Perl. На мой взгляд, иногда полезно иметь «лучший» шаблон для применения к конкретной проблеме.

Ответ №3:

Возможно, оптимизация выполняется преждевременно. Вы пробовали использовать профилировщик, такой как Devel::NYTProf, чтобы увидеть, где ваша программа проводит большую часть своего времени?

Комментарии:

1. Возможно, я преждевременно оптимизирую. Но опять же, этот скрипт находится в разработке более 10 лет. Ваш ответ действительно не отвечает на вопрос, который я задал.

Ответ №4:

Я предполагаю, что это tr/// было бы (немного) быстрее, чем s/// в вашем первом регулярном выражении. Насколько быстрее, конечно, будет определяться факторами, которых я не знаю о вашей программе и вашем окружении. Профилирование и сравнительный анализ ответят на этот вопрос.

Но если вы заинтересованы в каком-либо улучшении вашего кода, могу я предложить исправление для удобства сопровождения? Вы выполняете одну и ту же подстановку (или набор подстановок) для трех переменных. Это означает, что при изменении этой подстановки вам нужно менять ее три раза — а делать одно и то же три раза всегда опасно 🙂

Вы могли бы рассмотреть возможность рефакторинга кода, чтобы он выглядел примерно так:

 foreach ($val01, $val02, $val03) {
    s/[tnr]/ /g;
    s/"/""/g;
}
  

Кроме того, вероятно, было бы хорошей идеей иметь эти значения в массиве, а не в трех переменных с одинаковыми именами.

 foreach (@vals) {
    s/[tnr]/ /g;
    s/"/""/g;
}