为什么Perl子例程prototype `foo()`可以带参数?

jq6vz3qz  于 2023-10-24  发布在  Perl
关注(0)|答案(1)|浏览(145)
use 5.016;

sub foo() {
    say "foo";
}

sub main() {
    foo(42);
}

main();

如果我运行上面的代码,它将显示下面的错误,这是我所期望的,因为foo没有参数:

Too many arguments for main::foo at b.pl line 8, near "42)"
Execution of b.pl aborted due to compilation errors.
use 5.016;

sub main() {
    foo(42);
}

sub foo() {
    say "foo";
}

main()

但是如果我运行上面的代码,它可以正确地运行

foo

为什么foo这次接受参数?

ar5n3qh5

ar5n3qh51#

原型改变了函数的解析规则,所以为了被考虑,它们 * 必须 * 在函数调用被解析之前被知道,要么通过前面的定义,要么通过函数的预声明(sub foo();)。
如果一个函数调用出现在函数的定义或预声明之前,那么它只能被解析为一个普通的(非原型化的)调用,假设解释器可以首先识别出它是一个函数。(在这个例子中,它可以,因为括号。)
然后,一旦定义出现,所指示的原型对已经解析的调用没有影响。然而,在其定义(或预声明)之后,对函数的进一步调用被原型化。添加另一位:

use 5.016;

fun_proto(42);      # runtime warning: 
                    # "called too early to check prototype"

sub fun {
    fun_proto(42);  # parsed as a normal sub as no prototype has been seen
}

sub fun_proto() { 
    say "@_";
}

fun();              # fun_proto(42) call in 'fun' is OK

fun_proto(43);      # compilation error: extra argument by prototype

在函数定义之前的直接调用在运行时会产生一个警告,但在另一个sub中的调用不会。我不知道为什么从sub中的调用不会得到警告。(具有fun_proto(42)调用的sub的调用出现在fun_proto定义之后,但所有这些-封闭的sub和其中的fun_proto调用-都在定义之前编译。)
要修复类似这样的代码,请在函数的任何提及之前添加一个预声明(“forward declaration”)。
也许你很清楚你在做什么,但我不得不问:你的代码中真的需要原型吗?它们有微妙之处,而它们的目的仅仅是允许调用用户定义的Subs,就像调用内置函数一样,* 而不是检查参数的数量和类型 *。
如果目的是检查参数,那么从v5.20开始perl就有了正确的子例程签名。
†这样做的一个真实的问题是,代码的编译取决于原型,所以当它们没有在预期的时候被应用时(比如这里),可能会导致安静的错误。

sub fun() { ... }

当那个sub被调用时(不带括号),它调用之后的任何东西都不会被用作它的参数--因为它不接受参数!--所以

fun + 3     # intended to be compiled with regards to () prototype

后来(虽然很棘手)运行为

fun() + 3   # sub declared w/ prototype to take no arguments

然而,如果原型定义不适用于这个调用,就像这个问题中的例子一样,那么表达式中sub后面的内容 * 被 * 作为它的参数列表,所以代码被编译为

fun(+3)     # oups, not intended (prototype unused by error)

没有警告。

相关问题