Аргументы подпрограммы Perl
Я недавно читал о Perl и немного озадачен тем, как Perl обрабатывает аргументы, переданные подпрограммам.
В языке, таком как Python, Java или PHP, определение функции принимает форму (в псевдокоде):
function myFunc(arg1, arg2) {
// Do something with arg1 and arg2 here
}
Однако в Perl это просто:
sub mySub {
# @_ holds all arguments passed
}
И как я понимаю, это единственный способ сделать это.
-
Что делать, если я хочу ограничить вызывающий объект только двумя аргументами?
-
Разве это не просто Perl, не допускающий ничего, кроме аргументов с переменным числом на других языках (например, Python, C и т.д.)?
-
Разве это не проблема в какой-то момент?
-
Как насчет всех проверочных параметров аргументов по умолчанию на других языках? Нужно ли это делать прямо в Perl? Например,
sub a_sub {
if (@_ == 2) {
# Continue function
}
else {
return false
}
}
Ответы
Ответ 1
Вы опасаетесь среды Perl, потому что она сильно отличается от языков, с которыми вы сталкивались раньше.
Люди, которые верят в сильные типичные и прототипы функций, не согласятся здесь, но я считаю, что такие ограничения редко бывают полезны. Действительно ли C действительно поймал вас, передавая неправильное количество параметров функции, достаточно часто, чтобы быть полезной?
В современном Perl наиболее часто копируется содержимое @_
в список лексических скалярных переменных, поэтому вы часто увидите подпрограммы, начинающиеся с
sub mysub {
my ($p1, $p2) = @_;
... etc.
}
таким образом, все передаваемые параметры будут доступны как элементы @_
($_[0]
, $_[1]
и т.д.), в то время как ожидаемые названы и появятся в $p1
и $p2
(хотя я надеюсь, вы понимаете, что эти имена должны быть выбраны надлежащим образом).
В частном случае, когда подпрограмма является методом, первый параметр является особым. В других языках это self
или this
, но в Perl это просто первый параметр в @_
, и вы можете называть его тем, что вам нравится. В этих обстоятельствах вы увидите
sub method {
my $self = shift;
my ($p1, $p2) = @_;
... etc.
}
чтобы объект контекста (или имя класса, если он является методом класса) извлекается в $self
(имя, принятое условным обозначением), а остальные параметры остаются в @_
для доступа либо напрямую или, чаще всего, копируются в локальные скалярные переменные как $p1
, $p2
и т.д.
Чаще всего жалоба заключается в том, что проверки типа нет, поэтому я могу передать любой скаляр, который мне нравится, в качестве параметра подпрограммы. Пока use strict
и use warnings
находятся в контексте, даже это обычно легко отлаживать, просто потому, что операции, которые подпрограмма может выполнять на одной форме скаляра, обычно являются незаконными на другом.
Хотя первоначально это было больше связано с инкапсуляцией относительно объектно-ориентированного Perl, эта цитата из Larry Wall очень актуальна
Perl не имеет увлечения с принудительной конфиденциальностью. Вы предпочли бы, чтобы вы остались в своей гостиной, потому что вас не пригласили, а не потому, что у него есть дробовик.
C был спроектирован и реализован в те дни, когда это было большим повышением эффективности, если вы могли получить неисправную программу для сбоя во время компиляции, а не во время выполнения. Это изменилось сейчас, хотя подобная ситуация возникла с клиентским JavaScript, где было бы полезно знать, что код неправильный, прежде чем извлекать данные из Интернета, с которыми ему приходится иметь дело. К сожалению, проверка параметров JavaScript теперь слабее, чем должна быть.
Обновление
Для тех, кто сомневается в полезности Perl для учебных целей, я полагаю, что именно потому, что механизмы Perl настолько просты и прямолинейны, что они идеально подходят для таких целей.
-
Когда вы вызываете подпрограмму Perl, все параметры в вызове псевдонимы в @_
. Вы можете использовать их напрямую, чтобы повлиять на фактические параметры, или скопировать их, чтобы предотвратить внешнее действие.
-
Если вы вызываете подпрограмму Perl как метод, тогда вызывающий объект или класс предоставляется в качестве первого параметра. Опять же, подпрограмма (метод) может делать то, что ей нравится, с @_
Ответ 2
Perl не управляет вашей обработкой аргументов. Вместо этого он обеспечивает минимальную гибкую абстракцию и позволяет писать код, соответствующий вашим потребностям.
Передача по ссылке
По умолчанию Perl использует псевдоним для каждого аргумента в @_
. Это реализует базовую семантику по ссылке.
my $num = 1;
foo($num);
print "$num\n"; # prints 2.
sub foo { $_[0]++ }
Передача по ссылке выполняется быстро, но имеет риск утечки изменений в данные параметров.
Pass By Copy
Если вам нужна семантика передать по копии, вам нужно сделать копии самостоятельно. В сообществе Perl распространены два основных подхода к обработке списков позиционных параметров:
sub shifty {
my $foo = shift;
}
sub listy {
my ($foo) = @_;
}
В моем месте работы мы делаем версию listy:
sub fancy_listy {
my ($positional, $args, @bad) = @_;
die "Extra args" if @bad;
}
Именованные параметры
Другой распространенной практикой является использование параметров named:
sub named_params {
my %opt = @_;
}
Некоторые люди довольны только вышеизложенным. Я предпочитаю более подробный подход:
sub named_params {
my %opt = @_;
my $named = delete $opt{named} // "default value";
my $param = delete $opt{param}
or croak "Missing required 'param'";
croak "Unknown params:", join ", ", keys %opt
if %opt;
# do stuff
}
Это распаковывает именованные параметры в переменные, позволяет пространство для базовой проверки и значений по умолчанию и обеспечивает, чтобы никакие дополнительные неизвестные аргументы не были переданы.
На Perl Prototypes
Perl "прототипы" - это прототипы не в нормальном смысле. Они предоставляют только подсказки компилятора, которые позволяют пропускать скобки при вызове функций. Единственное разумное применение - имитировать поведение встроенных функций. Вы можете легко победить проверку аргументов прототипа. В общем, НЕ ИСПОЛЬЗУЙТЕ ПРОТОТИПЫ. Используйте их с осторожностью, чтобы использовать перегрузку оператора - т.е. экономно и только для улучшения удобочитаемости.
Ответ 3
По какой-то причине Perl любит списки и не любит статическую типизацию. Массив @_
фактически открывает большую гибкость, потому что аргументы подпрограммы передаются по ссылке, а не по значению. Например, это позволяет нам делать аргументы:
my $x = 40;
add_to($x, 2);
print "$x\n"; # 42
sub add_to { $_[0] += $_[1] }
... но это скорее исторический взлом производительности. Обычно аргументы "объявляются" назначением списка:
sub some_sub {
my ($foo, $bar) = @_;
# ^-- this assignment performs a copy
...
}
Это делает семантику этого вспомогательного вызова по значению, что обычно более желательно. Да, неиспользуемые аргументы просто забыты, и слишком мало аргументов не вызывает никакой автоматической ошибки - переменные просто содержат undef
. Вы можете добавить произвольную проверку, например. проверив размер @_
.
Существуют планы окончательно сделать именованные параметры доступными в будущем, которые будут выглядеть как
sub some_sub($foo, $bar) { ... }
Этот синтаксис можно использовать сегодня, если вы установите модуль signatures
. Но есть что-то еще лучше: я могу настоятельно рекомендовать Function::Parameters
, что позволяет использовать синтаксис как
fun some_sub($foo, $bar = "default value") { ... }
method some_method($foo, $bar, :$named_parameter, :$named_with_default = 42) {
# $self is autodeclared in methods
}
Это также поддерживает проверки экспериментального типа.
Расширения Parser FTW!
Ответ 4
Если вы действительно хотите наложить более строгие проверки параметров в Perl, вы можете посмотреть что-то вроде Params:: Validate.
Ответ 5
Perl имеет возможность прототипирование для замещающих параметров, которые вы как бы привыкли видеть, но часто не нужны.
sub foo($){
say shift;
};
foo(); # Error: Not enough arguments for main::foo
foo('bar'); # executes correctly
И если вы сделали sub foo($$){...}
, для этого потребовались бы 2 необязательных аргумента (например, foo('bar','baz')
)
Ответ 6
Вы можете просто использовать:
my ($arg1, $arg2) = @_;
Чтобы явно ограничить количество аргументов, которые вы можете использовать:
my $number =2;
die "Too many arguments" if @_ > $number;