Perl子程序参数

我最近一直在阅读关于Perl的知识,并且对Perl如何处理传递给子例程的参数有些困惑。

在像Python,Java或PHP这样的语言中,函数定义的forms是(以伪代码的forms):

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

您对Perl环境保持警惕,因为它与之前遇到的语言完全不同。

那些相信强大的打字和function原型的人会不同意这个观点,但是我认为这样的限制很less有用。 C 真的发现你通过错误的参数数量通常足够有用吗?

在现代Perl中最常见的是将@_的内容复制到词法标量variables列表中,因此您经常会看到子程序以

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

这样,传递的所有参数将作为@_$_[0]$_[1]等)的元素提供,而预期的参数被命名并出现在$p1$p2 (尽pipe我希望你明白这些名字应该适当select)。

在子例程是特定的情况下,第一个参数是特殊的。 在其他语言中,它是selfthis ,但是在Perl中它只是@_的第一个参数,你可以称它为你喜欢的。 在这种情况下,你会看到

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

所以上下文对象(或类的名称,如果它是一个类的方法)被提取到$self (通过约定假定的名称),其余的参数保留在@_直接访问,或更通常,复制到本地标量variables中,如$p1$p2

大多数情况下,投诉是没有types检查,所以我可以通过任何我喜欢的标量作为子程序参数。 只要use strictuse warnings的情况下,即使这通常很容易debugging,只是因为子例程可以在一个标量forms上执行的操作通常是另一种forms的非法。

虽然最初更多的是关于面向对象Perl的封装,但Larry Wall的引用非常相关

Perl没有强制隐私的迷恋。 它宁愿你留在客厅,因为你没有被邀请,不是因为它有一把猎枪

C的devise和实施是在一个主要的效率提升的日子,如果你可以得到一个错误的程序在编译期间而不是在运行时失败。 现在已经发生了变化,尽pipe客户端JavaScript也出现了类似的情况,在从互联网获取数据之前知道代码是错误的是非常有用的。 可悲的是,JavaScript参数检查现在比应该松散。


更新

对于那些怀疑Perl用于教学目的的用户,我build议, 正是因为 Perl的机制非常简单直接,所以它们非常适合这样的用途。

  • 在调用Perl子例程时,调用中的所有参数在@_都是别名 。 您可以直接使用它们来影响实际参数,也可以复制它们以防止外部操作

  • 如果你调用一个Perl子程序作为一个方法,那么提供的调用对象或类作为第一个参数。 再次,子程序(方法)可以用@_做它喜欢的

Perl不pipe理你的参数处理。 相反,它提供了一个最小的,灵活的抽象,并允许您编写适合您的需求的代码。

按参考传递

默认情况下,Perl会在@_为每个参数添加一个别名。 这实现了基本的, 通过引用语义。

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

通过引用是快速的,但具有泄漏参数数据变化的风险。

通过复制

如果要通过复制语义传递 ,则需要自己创build副本。 处理位置参数列表的两种主要方法在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 } 

这将命名参数解压缩为variables,允许用于基本validation和默认值的空间,并强制不传入额外的未知参数。

在Perl原型

Perl的“原型” 不是一般意义上的原型。 它们只提供编译器提示,允许您跳过函数调用的括号。 唯一合理的用途是模仿内置函数的行为。 您可以轻松击败原型参数检查。 一般来说, 不要使用原型 。 小心使用它们,你会使用运算符重载 – 也就是说,只是为了提高可读性。

由于某些原因,Perl喜欢列表,并且不喜欢静态types。 @_数组实际上打开了很大的灵活性,因为子程序参数是通过引用传递 ,而不是通过值传递 。 例如,这使我们可以做出不合理的论点:

 my $x = 40; add_to($x, 2); print "$x\n"; # 42 sub add_to { $_[0] += $_[1] } 

…但这更多的是一个历史性的performance。 通常,参数是通过列表分配“声明”的:

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

这使得这个子调用的语义,这通常是更可取的。 是的,未使用的参数被简单地遗忘了,太less的参数不会引发任何自动错误 – variables只包含undef 。 您可以添加任意validation,例如通过检查@_的大小。


目前有计划最终使命名参数在将来可用,看起来像

 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 } 

这也支持实验types检查。

parsing器扩展FTW!

如果你真的想在Perl中join更严格的参数检查,你可以看看像Params :: Validate这样的东西。

Perl确实具有参数占位符的原型function,您可以习惯于查看,但通常是不必要的。

 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;