На Perl аргументов подпрограммы

Я недавно читал о Perl и немного озадачен тем, как Perl обрабатывает аргументы, переданные подпрограммам.

в языке, таком как Python, Java или PHP, определение функции принимает форму (в псевдокоде):

function myFunc(arg1, arg2) {
    // Do something with arg1 and arg2 here
}

но в Perl это просто:

sub mySub {
    # @_ holds all arguments passed
}

и как я понимаю, это единственный способ сделать это.

  • что если я хочу ограничить абоненту пройти только 2 споры?

  • разве это не просто Perl не позволяет ничего, кроме аргументов с переменным числом на других языках (например, Python, C и т. д.)?

  • не станет ли это проблемой в какой-то момент?

  • как насчет проверки всех аргументов по умолчанию на других языках? Нужно ли делать это явно в Perl? Например,

    sub a_sub {
        if (@_ == 2) {
            # Continue function
        }
        else {
            return false
        }
    }
    

7 ответов


вы опасаетесь среды Perl, потому что она сильно отличается от языков, с которыми вы сталкивались раньше.

люди, которые верят в сильные прототипы типов и функций, не согласятся здесь, но я считаю, что такие ограничения редко полезны. Имеет C действительно поймал вас, передавая неправильное количество параметров функции достаточно часто, чтобы быть полезным?

наиболее распространено в современном Perl копировать содержимое @_ к списку лексических скалярных переменных, поэтому вы часто увидите подпрограммы, начинающиеся с

sub mysub {
  my ($p1, $p2) = @_;
  ... etc.
}

таким образом, все передаваемые параметры будут доступны в виде элементов @_ ($_[0], $_[1] etc.), а ожидается они называются и появляются в $p1 и $p2 (хотя я надеюсь, вы понимаете, что эти имена должны быть выбраны соответствующим образом).

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

sub method {
  my $self = shift;
  my ($p1, $p2) = @_;
  ... etc.
}

так что объект контекста (или имя класса, если это метод класса) извлекается в $self (имя, принятое по соглашению), а остальные параметры остаются в @_ для прямого доступа или, как правило,, скопировано в локальные скалярные переменные как $p1, $p2 etc.

чаще всего жалоба заключается в том, что нет проверки типа, поэтому я могу передать любой скаляр, который мне нравится, как параметр подпрограммы. Пока use strict и use warnings находятся в контексте, даже это обычно просто отлаживать, просто потому, что операции, которые подпрограмма может выполнять на одной форме скаляра, обычно незаконны на другой.

хотя первоначально это было больше связано с инкапсуляцией с что касается объектно-ориентированного Perl, эта цитата из Ларри Уолла очень актуальна

у Perl нет увлечения принудительной приватностью. Он предпочел бы, чтобы вы остались вне его гостиной, потому что вы не были приглашены, а не потому, что у него есть дробовик

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


обновление

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

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

  • если вы вызываете подпрограмму Perl как метод затем вызывающий объект или класс как первый параметр. Опять же, подпрограмма (метод) может делать то, что ей нравится с @_


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

Проходим По Ссылке

по умолчанию Perl вставляет псевдоним для каждого аргумента в @_. Это реализует основные, проходим по ссылке семантика.

my $num = 1;
foo($num);
print "$num\n";  # prints 2.

sub foo { $_[0]++ }

Pass по ссылке быстро, но имеет риск утечки изменений в данные параметров.

Проходим Мимо Копия

если вы хотите пройти мимо copy семантика, вам нужно сделать копии самостоятельно. В сообществе Perl распространены два основных подхода к обработке списков позиционных параметров:

sub shifty {
    my $foo = shift;
}

sub listy {
    my ($foo) = @_;
}

на моем месте работы мы делаем версию listy:

sub fancy_listy {

    my ($positional, $args, @bad) = @_;

    die "Extra args" if @bad;
}

Именованные Параметры

Другой распространенной практикой является использование именованные параметры:

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

"прототипы" Perl являются не прообразы в обычном смысле. Они предоставляют только подсказки компилятора, которые позволяют пропустить скобки при вызовах функций. Единственное разумное использование-имитировать поведение встроенных функций. Вы можете легко победить проверку аргументов прототипа. В общем, НЕ ИСПОЛЬЗОВАТЬ ПРОТОТИПЫ. Используйте их с осторожностью, чтобы использовать перегрузку оператора-т. е. экономно и только для улучшения читаемости.


по какой-то причине Perl любит списки и не любит статическую типизацию. The @_ массив фактически открывает большую гибкость, потому что аргументы подпрограммы передаются по ссылке, а не по стоимости. Например, это позволяет нам делать-аргументы:

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
  ...
}

это делает семантику этого sub call-by-value, что обычно более желательно. Да, неиспользуемые аргументы просто забываются, и слишком мало аргументов не вызывают автоматической ошибки – переменные просто содержат 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
}

это также поддерживает проверки экспериментального типа.

расширения парсера FTW!


Если вы действительно хотите ввести более строгие проверки параметров в Perl, вы можете посмотреть что-то вроде Params:: Validate.


У Perl есть прототипирование возможность для заполнителей параметров, которые вы привыкли видеть, но это часто не нужно.

sub foo($){
    say shift;
}; 
foo();      # Error: Not enough arguments for main::foo
foo('bar'); # executes correctly

и если вы сделали sub foo($$){...} для этого потребуется 2 необязательных аргумента (например,foo('bar','baz'))


Вы можете просто использовать:

my ($arg1, $arg2) = @_;

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

my $number =2;
die "Too many arguments" if @_ > $number;

Если Вы читаете о perl в последнее время, пожалуйста, прочитайте последние perl. Вы можете прочитать В Современном Языке Perl книги бесплатно, а также: бесплатная онлайн современная книга Perl

вот несколько примеров из этой книги о подписи функции:

sub greet_one($name = 'Bruce') { 
    say "Hello, $name!"; 
}
sub greet_all($leader, @everyone) { 
    say "Hello, $leader!"; 
    say "Hi also, $_." for @everyone; 
}
 sub make_nested_hash($name, %pairs) { 
    return { $name => \%pairs }; 
}