简体   繁体   English

Perl 子程序参数

[英]Perl subroutine arguments

I have been reading about Perl recently and am slightly perplexed about how Perl handles arguments passed to subroutines.我最近一直在阅读有关 Perl 的文章,但对 Perl 如何处理传递给子例程的参数感到有些困惑。

In a language like Python, Java or PHP, a function definition takes the form (in pseudocode):在 Python、Java 或 PHP 等语言中,函数定义采用以下形式(伪代码):

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

Yet in Perl it's just:然而在 Perl 中,它只是:

sub mySub {
    # @_ holds all arguments passed
}

And as I understand it, that's the only way to do it.据我所知,这是唯一的方法。

  • What if I want to restrict the caller to only pass two arguments?如果我想限制调用者只传递两个参数怎么办?

  • Isn't this just Perl not allowing anything but variable-number arguments in other languages (ie, Python, C, etc.)?这难道不是 Perl 只允许其他语言(即 Python、C 等)中的可变数参数以外的任何东西吗?

  • Wouldn't that become a problem at some point?这在某些时候不会成为问题吗?

  • What about all the default argument-number checking in other languages?其他语言中的所有默认参数编号检查怎么样? Would one have to do that explicitly in Perl?是否必须在 Perl 中明确地做到这一点? For instance例如

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

You are wary of the Perl environment because it is quite different from the languages you have come across before.您对 Perl 环境持谨慎态度,因为它与您之前遇到的语言大不相同。

The people who believe in strong typing and function prototypes will disagree here, but I believe that restrictions like that are rarely useful.相信强类型和函数原型的人在这里会不同意,但我相信这样的限制很少有用。 Has C really caught you passing the wrong number of parameters to a function often enough to be useful? C 是否真的发现您经常向函数传递错误数量的参数以致于有用?

It is most common in modern Perl to copy the contents of @_ to a list of lexical scalar variables, so you will often see subroutines starting with在现代 Perl 中最常见的是将@_的内容复制到词法标量变量列表中,因此您经常会看到子程序以

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

that way, all parameters that are passed will be available as elements of @_ ( $_[0] , $_[1] etc.) while the expected ones are named and appear in $p1 and $p2 (although I hope you understand that those names should be chosen appropriately).这样,所有传递的参数都可以作为@_$_[0]$_[1]等)的元素使用,而预期的参数被命名并出现在$p1$p2 (尽管我希望你理解应该适当地选择这些名称)。

In the particular case that the subroutine is a method , the first parameter is special.在子程序是方法的特殊情况下,第一个参数是特殊的。 In other languages it is self or this , but in Perl it is simply the first parameter in @_ and you may call it what you like.在其他语言中,它是selfthis ,但在 Perl 中,它只是@_的第一个参数,您可以@_称呼它。 In those circumstances you would see在这些情况下你会看到

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

so that the context object (or the name of the class if it is a class method) is extracted into $self (a name assumed by convention) and the rest of the parameters remain in @_ to be accessed either directly or, more usually, copied to local scalar variables as $p1 , $p2 etc.以便上下文对象(或类的名称,如果它是类方法)被提取到$self (约定假定的名称)中,其余参数保留在@_以便直接访问,或者更常见的是, 复制到局部标量变量$p1 , $p2等。

Most often the complaint is that there is no type checking either, so I can pass any scalar I like as a subroutine parameter.最常见的抱怨是也没有类型检查,所以我可以传递任何我喜欢的标量作为子程序参数。 As long as use strict and use warnings are in context, even this is generally simple to debug, simply because the operations that the subroutine can perform on one form of scalar are usually illegal on another.只要use strictuse warnings在上下文中,即使这样通常也很容易调试,仅仅是因为子例程可以对一种形式的标量执行的操作在另一种形式上通常是非法的。

Although it was originally more to do with encapsulation with respect to object-oriented Perl, this quote from Larry Wall is very relevant虽然它最初更多地与面向对象 Perl 的封装有关,但 Larry Wall 的这句话非常相关

Perl doesn't have an infatuation with enforced privacy. Perl 不迷恋强制隐私。 It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun它宁愿你呆在客厅外面,因为你没有被邀请,而不是因为它有一把猎枪

C was designed and implemented in the days when it was a major efficiency boost if you could get a faulty program to fail during compilation rather than at run time. C 的设计和实现是在那个时代,如果您可以让错误的程序在编译期间而不是在运行时失败,它会大大提高效率。 That has changed now, although a similar situation has arisen with client-side JavaScript where it actually would be useful to know that the code is wrong before fetching the data from the internet that it has to deal with.现在已经改变,虽然类似的情况已经与客户端JavaScript出现在那里,它实际上知道的代码是从它必须处理与互联网获取数据之前错误有用。 Sadly, JavaScript parameter checking is now looser than it should be.可悲的是,JavaScript 参数检查现在比它应该的要松散。


Update更新

For those who doubt the usefulness of Perl for teaching purposes, I suggest that it is precisely because Perl's mechanisms are so simple and direct that they are ideal for such purposes.对于那些怀疑 Perl 用于教学目的的人,我建议正是因为Perl 的机制非常简单和直接,所以它们非常适合此类目的。

  • When you call a Perl subroutine all of the parameters in the call are aliased in @_ .当您调用 Perl 子例程时,调用中的所有参数都以@_别名 You can use them directly to affect the actual parameters, or copy them to prevent external action您可以直接使用它们来影响实际参数,或者复制它们以防止外部动作

  • If you call a Perl subroutine as a method then the calling object or class is provided as the first parameter.如果您将 Perl 子例程作为方法调用,则调用对象或类将作为第一个参数提供。 Again, the subroutine (method) can do what it likes with @_同样,子程序(方法)可以用@_做它喜欢做的事

Perl doesn't manage your argument handling for you. Perl 不会为您管理参数处理。 Instead, it provides a minimal, flexible abstraction and allows you to write code that fits your needs.相反,它提供了一个最小的、灵活的抽象,并允许您编写适合您需要的代码。

Pass By Reference通过引用

By default, Perl sticks an alias to each argument in @_ .默认情况下,Perl 为@_每个参数添加一个别名。 This implements basic, pass by reference semantics.这实现了基本的按引用传递语义。

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

sub foo { $_[0]++ }

Pass by reference is fast but has the risk of leaking changes to parameter data.通过引用传递速度很快,但存在泄露参数数据更改的风险。

Pass By Copy通过复制

If you want pass by copy semantics, you need to make the copies yourself.如果您想通过复制语义传递,您需要自己制作副本。 Two main approaches to handling lists of positional parameters are common in the Perl community:处理位置参数列表的两种主要方法在 Perl 社区中很常见:

sub shifty {
    my $foo = shift;
}

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

At my place of employment we do a version of listy:在我工作的地方,我们做了一个 listy 版本:

sub fancy_listy {

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

    die "Extra args" if @bad;
}

Named Parameters命名参数

Another common practice is the use of named parameters :另一种常见做法是使用命名参数

sub named_params {
    my %opt = @_;
}

Some people are happy with just the above.有些人对以上内容感到满意。 I prefer a more verbose approach:我更喜欢更详细的方法:

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 
}

This unpacks named params into variables, allows space for basic validation and default values and enforces that no extra, unknown arguments were passed in.这将命名参数解包为变量,为基本验证和默认值留出空间,并强制不传入额外的未知参数。

On Perl Prototypes Perl 原型

Perl's "prototypes" are not prototypes in the normal sense. Perl 的“原型”不是通常意义上的原型。 They only provide compiler hints that allow you to skip parenthesis on function calls.它们只提供编译器提示,允许您跳过函数调用的括号。 The only reasonable use is to mimic the behavior of built-in functions.唯一合理的用途是模仿内置函数的行为。 You can easily defeat prototype argument checking.您可以轻松击败原型参数检查。 In general, DO NOT USE PROTOTYPES .一般来说,不要使用原型 Use them with with care that you would use operator overloading--ie sparingly and only to improve readability.小心使用它们,因为您会使用运算符重载——即谨慎使用,仅用于提高可读性。

For some reason, Perl likes lists, and dislikes static typing.出于某种原因,Perl 喜欢列表,而不喜欢静态类型。 The @_ array actually opens up a lot of flexibility, because subroutine arguments are passed by reference , and not by value . @_数组实际上提供了很大的灵活性,因为子程序参数是通过引用传递,而不是通过值传递。 For example, this allows us to do out-arguments:例如,这允许我们做out-arguments:

my $x = 40;
add_to($x, 2);
print "$x\n"; # 42

sub add_to { $_[0] += $_[1] }

… but this is more of an historic performance hack. ……但这更像是一个历史性的性能黑客。 Usually, the arguments are “declared” by a list assignment:通常,参数由列表赋值“声明”:

sub some_sub {
  my ($foo, $bar) = @_;
  #               ^-- this assignment performs a copy
  ...
}

This makes the semantics of this sub call-by-value, which is usually more desirable.这使得这个子按值调用的语义,这通常是更可取的。 Yes, unused arguments are simply forgotten, and too few arguments do not raise any automatic error – the variables just contain undef .是的,未使用的参数只是被遗忘了,太少的参数不会引发任何自动错误——变量只包含undef You can add arbitrary validation eg by checking the size of @_ .您可以添加任意验证,例如通过检查@_的大小。


There exist plans to finally make named parameters available in the future, which would look like有计划在未来最终使命名参数可用,看起来像

sub some_sub($foo, $bar) { ... }

You can have this syntax today if you install the signatures module.如果您安装了signatures模块,您今天就可以使用这种语法。 But there is something even better: I can strongly recommend Function::Parameters , which allows syntax like但还有更好的东西:我强烈推荐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
}

This also supports experimental type checks.这也支持实验类型检查。

Parser extensions FTW!解析器扩展 FTW!

如果您真的想在 Perl 中强加更严格的参数检查,您可以查看类似Params::Validate 的内容

Perl does have the prototyping capability for parameter placeholders, that you're kind of used to seeing, but it's often unnecessary. Perl 确实具有参数占位符的原型设计功能,您已经习惯了这种功能,但通常没有必要。

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

And if you did sub foo($$){...} it would require 2 non-optional arguments (eg foo('bar','baz') )如果你做了sub foo($$){...}它将需要 2 个非可选参数(例如foo('bar','baz')

You can just use:你可以只使用:

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

To explicitly limit the number of arguments you can use:要明确限制您可以使用的参数数量:

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

If you are reading about Perl recently, please read about recent Perl.如果您最近正在阅读 Perl,请阅读最近的 Perl。 You may read the Modern Perl book for free as well.您也可以免费阅读Modern Perl书籍。

Here are a few examples from that book about function signatures:以下是该书中关于函数签名的一些示例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM