Адаптивная подпрограмма
На этом примере я хотел бы изучить лучший метод кодирования адаптивной подпрограммы.
Мне нужна подпрограмма, которая обрезает текст (на самом деле, это предлог, моя подпрограмма могла бы сделать что-нибудь еще).
Чтобы быть более универсальным, я хотел бы, чтобы моя подпрограмма принимала различные типы аргументов:
my @input = (' A ', ' B', 'C ');
my @trimmed = trim(@input);
trim(\@input);
my $out = trim($input[0]);
который возвращает "А"trim(\$input[0]);
который обрезает первый элемент @input;trim(\@a, \@b, \@c);
Каждый массив строк обрезаетсяmy $out = trim(\@a, ' A ', ' B');
$out = (qw/A B/); (поведение будет обсуждаться)
Вот мое текущее уродливое решение:
sub trim {
state $re = qr/^\s+|\s+$/m;
my @a;
for(@_) {
if(ref eq 'SCALAR') { $$_ =~ s/$re//g; }
elsif(ref) { trim(\$_) for(@$_); }
else { push @a, s/$re//gr; }
}
return \@a if @a > 1;
return $a[0] if $a[0];
}
Есть ли лучшее решение этой реализации, которое поддерживает различные виды ввода, как я предлагал выше?
Основная причина этого вопроса касается моего последнего приложения, в котором я несу текст, который может быть сохранен в виде строки, массива строк или даже хеша.
Я полагаю, лучше написать:
trim(\@allmytexts);
align(\@allmytexts, align=>'right');
чем:
for(@allmytexts) {
trim($_);
align($_, align=>'right');
}
2 ответа
Этот вопрос не по теме и, скорее всего, скоро будет закрыт.
Тем не менее, "Правильно ли запрашивать такой тип поведения в таких подпрограммах?"
Я бы сказал нет. Я далек от понимания того, что вы хотите, чтобы эта подпрограмма относилась к информации, находящейся передо мной, и, не обращаясь к вашей документации, я бы наверняка не мог вспомнить, как вызывать ее, если бы не использовал ее для день или два. Вы обнаружите, что все используют это, написав
$string = trim($string)
потому что это то, что они помнят.
Похоже, вы сами не знаете, что делают некоторые звонки, следовательно,
my $out = trim(\@a, ' A ', ' B'); $out = (qw/A B/); (behavior to be discussed)
Я подозреваю, что вы привыкли к языку с другим механизмом передачи, и что-то вроде этого было бы полезно. В Perl все передается псевдонимом; поэтому операция над элементом @_
массив эквивалентен той же операции с фактическим параметром. Это означает, что подпрограмма может изменить любое записываемое значение, если вы передадите это значение в качестве параметра.
Имея это в виду, передача ссылки для указания того, что она должна быть изменена на месте, является не чем иным, как флагом, который определяет поведение подпрограммы вместе с обременением того, что ссылка должна быть разыменована перед тем, как ее можно будет использовать.,
Спросите себя, как часто вы видели, как часто используемые библиотечные функции ведут себя так. Например, есть отдельные звонки index
а также rindex
делать очень похожие вещи, и существующий основной оператор lc
разумно похоже на то, что trim
пытается сделать, но он принимает только один параметр и возвращает преобразованный результат. sprintf
можно рассматривать как вариант printf
Если бы он только знал, что, если параметр дескриптора файла является ссылкой на скаляр, он должен поместить строку в переменную, а не записывать ее в дескриптор файла. Но это не было разработано так.
Я могу только предложить альтернативное решение определенной проблемы. Вы не объясняете реальную трудность, и так как вы сами не уверены, какова ваша trim
Должен ли я сомневаться, если кто-то может помочь вам улучшить его.
Что-то еще, чтобы спросить себя, что должно быть возвращаемым значением, когда ссылка передана, и значение изменено на месте? Может быть очевидно, что он должен возвращать значение после его изменения, но, поскольку вы пишете адаптивный код, как насчет изменения возвращаемого значения в зависимости от того, был ли вызов в скалярном, списочном или пустом контексте? Или, еще лучше, сделать trim
подпрограмма lvalue, так что вы можете написать что-то вроде
my $n = q{ 999 };
++trim($n);
Звучит слишком сложно для меня. Использование должно быть легче читать и запоминать, чем это![1]. Просто иметь trim
взять скаляр (по умолчанию $_
).
sub trim(_) { $_[0] =~ s/^\s+|\s+\z//rg }
Все ваши дела могут быть легко обработаны, начиная с
$x = trim($x);
@a = map trim, @a;
Вы можете сделать версию на месте тоже.
sub trim_i(_) { s/^\s+|\s+\z//g for $_[0] }
Это будет использоваться следующим образом:
trim_i($x);
trim_i for @a;
Другая возможность - взять список скаляров (по умолчанию нет).
sub trim {
wantarray
? map s/^\s+|\s+\z//rg, @_
: $_[0] =~ s/^\s+|\s+\z//rg
}
$x = trim($x);
@a = trim(@a);
sub trim_i {
s/^\s+|\s+\z//rg for @_;
}
trim_i($x);
trim_i(@a);
Но это немного странно. Особенно с тех пор, как my $x = trim($y, $z);
возможно.
- Кроме того, он использует полиморфизм на основе типов, что не очень хорошая идея в Perl. Ваш код ведет себя неожиданно, когда сталкивается с объектом, который перегружает строковое преобразование.