2016-09-23 3 views
2

У меня есть этот цикл:Оптимизация троичного оператора в Perl

for my $line (split /\n/, $content) { 
    ($line !~ /^\-{2,}$/) ? ($return .= "$line\n") 
          : ($return .= "\N{ZERO WIDTH SPACE}$line\n"); 
} 

Там будет в основном линии, которые не соответствуют регулярному выражению (т.е. большую часть времени условие будет истинным).

Я первый написал условие, используя =~ оператор (с двумя условными инструкциями местами), но тогда это вторая инструкция была бы выполнена в большинстве случаев.

Другими словами ... Когда у вас есть тест, который вы знаете, что он выберет одну ветвь в 99% случаев, изменит ли она что-то (производительность), чтобы написать его с этой ветвью?

+1

Эти круглые скобки не являются необходимыми, они делают вид, что что-то еще происходит. Легче читать без них. – Schwern

+1

Предположительно, вы прочитали весь файл в '$ content', изменив' $/'? Если это так, вы можете просто оставить '$ /' как есть и прочитать файл по строкам. Это также имеет то преимущество, что вам не нужно добавлять новую строку в конец каждой выходной строки. – Borodin

+2

Дефис '-' в шаблоне регулярного выражения не обязательно должен быть экранирован. – Borodin

ответ

3

Если у вас есть тест, который вы знаете, что он будет выбрать одну ветвь в 99% случаев, это что-то изменить (производительность), чтобы написать его с этой отрасли в первую очередь?

В простой, если/другое дело (что тройная оператор), ответ нет. Порядок ветвей не имеет значения, условие будет выполняться каждый раз и выбирать, какая ветвь должна опускаться.

В случае if/elsif/else это имеет значение, так как существует несколько условных выражений для запуска. Ввод наиболее распространенного случая в первую очередь сделает вещи быстрее.

Если if/else выбирает заказ, который имеет наибольший смысл для читателя, и это обычно означает избежание негативов. $line =~ /^\-{2,}$/ легче читать, чем $line !~ /^\-{2,}$/. $line =~ /^-{2,}$/ еще лучше (нет необходимости скрывать - в регулярном выражении).

По крайней мере, он не должен иметь значения. Как с чем-то сложным, как Perl, лучше всего сравнить эти вещи. Немного хлопотно придумать что-то, что будет использовать процессор достаточно, чтобы не потеряться в нормальном джиттере бенчмаркинга. Не забудьте запустить это несколько раз с большим количеством итераций, прежде чем делать выводы.

use strict; 
use warnings; 
use v5.10; 

use Benchmark qw(cmpthese); 

my $Iterations = shift; 

my $Threshhold = 100_000; 

# I've picked something that isn't constant to avoid constant folding 
sub a_then_b { 
    my $num = shift; 
    return $num > $Threshhold ? sqrt($num) + sqrt($num) ** 2 
           : $num + $num; 
} 

sub b_then_a { 
    my $num = shift; 
    return $num <= $Threshhold ? $num + $num 
           : sqrt($num) + sqrt($num) ** 2; 
} 

say "First one side"; 
cmpthese $Iterations, { 
    a_then_b => sub { a_then_b($Threshhold - 1) }, 
    b_then_a => sub { b_then_a($Threshhold - 1) } 
}; 

say "Then the other"; 
cmpthese $Iterations, { 
    a_then_b => sub { a_then_b($Threshhold + 1) }, 
    b_then_a => sub { b_then_a($Threshhold + 1) } 
}; 

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

$return .= $line =~ /^-{2,}$/ ? "\N{ZERO WIDTH SPACE}$line\n" 
           : "$line\n"; 
3

То, что вы можете думать о том, что в цепи if ... elsif ... elsif ... else, он является наиболее эффективным, если тесты написаны в порядке убывания вероятности. Это минимизирует ожидаемое количество тестов и должно привести к более быстрому коду. Но в вашем случае у вас есть только один тест, поэтому он уже отсортирован, и инвертирование логики этого теста не имеет значения.

В любом случае вы беспокоитесь о деталях слишком много, чтобы внести существенные изменения. Вы всегда должны написать весь код, чтобы быть ясно и читаемым как можно

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

Если ваш код необходимо оптимизировать, вы должны начать с профилирования его, чтобы найти узкие места. У меня есть never нашел инвертирование смысла условного на любое использование. Независимо от того, какой язык вы используете, я бы ожидал, что разница между веткой и не-веткой будет незначительной.

Я бы предпочел увидеть более идиоматический Perl. В дополнение к чтению вашего файла построчно, как я писал в моем комментарии выше, я хотел бы использовать по умолчанию переменной $_ и добавить нулевую ширину пространства независимо от остальной части линии

for (split /\n/, $content) { 
    $return .= "\N{ZERO WIDTH SPACE}" if /^-{2,}$/; 
    $return .= "$_\n"; 
} 
+0

Thx. С вашим кодом я бы не потерял EOL? Разделение сохраняет его? –

+0

@ Стефан: Прошу прощения; Я не рядом с рабочим компьютером, поэтому я не смог проверить свой код. Теперь это исправлено, но было бы намного проще, если бы строки были прочитаны в цикле 'while' прямо из дескриптора файла. Как бы то ни было, вы делаете несколько копий своего файла. – Borodin

+0

my $ re = qr/^ - {2,} $/o; my $ return = join "\ n", map {$ _ = "\ N {ZERO WIDTH SPACE}". $ _ if $ _ = ~ $ re; } split (/ \ n /, $ content); –

Смежные вопросы