Введение:Лучшая практика именования в Perl для подпрограммы с выходными аргументами
Я обычно не используют выходные аргументы (аргументы, которые действуют в качестве входных и выходных значений или в качестве выхода только). Это для улучшения удобочитаемости и ремонтопригодности моего кода. Но опять же, на днях у меня внезапно возникла глубоко вложенная подпрограмма. И я попытался оптимизировать его, чтобы получить некоторые улучшения скорости.
Вот пример: у меня есть строка, представляющая строку обрезанного пробела, считанную из файла. Если строка пуста, я хотел бы заменить ее символом unicode, представляющим ключ возврата. Предположим, что я искал в Google и обнаружил, что символ ↵
(юникод U+21B5
) выглядит хорошо [1]. Поэтому я написал короткую подпрограмму:
sub handle_empty_lines {
my ($str) = @_;
if ((!defined $str) || $str eq '') {
return "\x{21B5}";
}
return $str;
}
и я использовал его, как это:
$line = handle_empty_lines($line);
Теперь я хотел бы, чтобы оптимизировать этот вызов, но все-таки есть код быть читаемым и ремонтопригодны.
Первый вариант ставит его рядный:
$line = "\x{21B5}" if (!defined $str) || $str eq '';
, конечно, естественно и быстро, но давайте предположим, что я не хочу загромождать код, если заявления и отверг этот вариант.
Вот два других варианта,
передать ссылку на
$str
, чтобы избежать копирования входной аргумент в подпрограмме (то есть: преобразовать вызов по значению для вызова по ссылке), илииспользовать встроенный вызов Perl посредством ссылки механизм.
Оба варианта вводит, "input/output argument"
(то есть: аргумент, который действует как вход и выход) и, таким образом, снижает читаемость кода и делает обслуживание более трудным (на мой взгляд).
Позвольте третьему варианту сохранить исходную версию (вызов по значению). Ниже приведено краткое сравнение трех вариантов только скорости (не читаемость).
use strict;
use warnings;
use Benchmark qw(timethese);
my $str1 = '';
timethese(
2_000_000,
{
case1 => sub { my $test = $str1; case1(\$test) },
case2 => sub { my $test = $str1; case2($test) },
case3 => sub { my $test = $str1; $test = case3($test) },
}
);
sub case1 {
if ((!defined $$_[0]) || $$_[0] eq '') {
$$_[0] = "\x{21B5}";
}
}
sub case2 {
if ((!defined $_[0]) || $_[0] eq '') {
$_[0] = "\x{21B5}";
}
}
sub case3 {
my ($str) = @_;
if ((!defined $str) || $str eq '') {
return "\x{21B5}";
}
return $str;
}
Выход (Ubuntu ноутбук, Intel (R) ядра (ТМ) i7-4702MQ процессора @ 2.20GHz):
Benchmark: timing 2000000 iterations of case1, case2, case3...
case1: 1 wallclock secs (0.84 usr + 0.00 sys = 0.84 CPU) @ 2380952.38/s (n=2000000)
case2: 1 wallclock secs (0.45 usr + 0.00 sys = 0.45 CPU) @ 4444444.44/s (n=2000000)
case3: 1 wallclock secs (0.70 usr + 0.00 sys = 0.70 CPU) @ 2857142.86/s (n=2000000)
Обратите внимание, что случай 2 составляет 87% быстрее, чем случай 1 и на 56% быстрее, чем случай 3.
Интересно, что вызов по ссылке (случай 1) медленнее, чем вызов по значению (случай 3).
Вопрос:
Предположим теперь я хотел бы держать случай 2:
sub handle_empty_lines {
if ((!defined $_[0]) || $_[0] eq '') {
$_[0] = "\x{21B5}";
}
}
Тогда, если я называю это с помощью:
handle_empty_lines($line);
это не дает ключ к разгадке читатель, что он изменяет $line
.
Как мне с этим справиться? Я могу думать о двух вариантах:
Поместите комментарий после вызова:
handle_empty_lines($line); # Note: modifies $line
Изменить название подпрограммы. Дайте имя, которое дает указание на читателя, что
$line
модифицируется, например:handle_empty_lines__modifies_arg($line);
Сноски:
1. Позже я узнал, что я мог бы использовать N{}
escape, чтобы сделать код более читаемым, используя «\ N {СТРЕЛКА ВНИЗ С УГОЛОВНЫМИ ЛЕВОМЯМИ]} вместо« \ x {21B5} »
2. Для этого простого случая, я согласен, что это сомнительно, если это можно назвать любую форму беспорядком ..
3. 4444444,44/2380952,38 = 1,87
Re "* Обратите внимание, что случай 2 составляет 87% 3 быстрее, чем случай 1, и на 56% быстрее, чем случай 3. *". Было бы намного легче заметить это, если бы вы использовали 'cmpthese' вместо' timethese' – ikegami
@ikegami Да, я рассматривал использование 'cmpthese', но я обнаружил, что цифры меня смутили –
Каждая из ваших функций отличается от других. Неопределенно отсутствует: 'sub case4 { if ((! Defined $ _ [0]) || $ _ [0] eq '') { return" \ x {21B5} "; } return $ _ [0]; } ' – ikegami