Аргументы подпрограммы 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 } } 

Вы опасаетесь среды 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. } 

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

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

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

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

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


Обновить

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

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

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

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

Прохождение по ссылке

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

 my $num = 1; foo($num); print "$num\n"; # prints 2. sub foo { $_[0]++ } 

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

Пропустить мимо копирования

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

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

На моем месте работы мы делаем версию списка:

 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 Prototypes

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

По какой-то причине 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!

Если вы действительно хотите ввести более строгие проверки параметров в 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. Вы также можете бесплатно ознакомиться с книгой Modern Perl : бесплатно онлайн Modern Perl book

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

 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 }; } 
  • Как выполнить подстановку Perl в строке при сохранении оригинала?
  • Какие псевдооператоры существуют в Perl 5?
  • Подсчет вхождений в первый столбец файла
  • Почему XML :: Simple «Disouraged»?
  • Perl читается по строкам
  • Как эффективно анализировать CSV-файл в Perl?
  • Почему прототипы функции Perl 5 плохо?
  • Как очистить файл на Perl?
  • Как я могу использовать новый модуль Perl без разрешения на установку?
  • Удаление цветовых кодов ANSI из текстового потока
  • Использование «системы» perl
  • Давайте будем гением компьютера.