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)。
在子例程是特定的情况下,第一个参数是特殊的。 在其他语言中,它是self
或this
,但是在Perl中它只是@_
的第一个参数,你可以称它为你喜欢的。 在这种情况下,你会看到
sub method { my $self = shift; my ($p1, $p2) = @_; ... etc. }
所以上下文对象(或类的名称,如果它是一个类的方法)被提取到$self
(通过约定假定的名称),其余的参数保留在@_
直接访问,或更通常,复制到本地标量variables中,如$p1
, $p2
等
大多数情况下,投诉是没有types检查,所以我可以通过任何我喜欢的标量作为子程序参数。 只要use strict
和use 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;