重新定义子程序时没有足够的参数
Not enough arguments when redefining a subroutine
当我重新定义自己的子例程(而不是 Perl 内置函数)时,如下所示:
perl -ce 'sub a($$$){} sub b {a(@_)}'
我收到此错误:
Not enough arguments for main::a at -e line 1, near "@_)"
我想知道为什么。
编辑:
"redefine"这个词可能选错了。但就我而言(我可能应该解释一下我最初尝试做的事情),我想通过在测试结果。
这是我所做的:
Test::More.pm :
sub is ($$;$) {
my $tb = Test::More->builder;
return $tb->is_eq(@_);
}
MyModule.pm :
sub is ($$;$) {
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is(@_);
}
你在这里没有重新定义任何东西。
你已经通过说 sub a($$$)
为你的子 a
设置了 prototype。函数定义中的美元符号告诉 Perl 这个 sub 恰好有三个标量参数。当你用 a(@_)
调用它时,Perl 不知道该列表中将有多少元素,因此它不知道调用将有多少参数,并在编译时失败。
不要乱用原型。你可能不需要它们。
相反,如果您知道您的子程序需要三个参数,请在您调用它的地方明确获取它们。
sub a($$$) {
...
}
sub b {
my ($one, $two, $three) = @_;
a($one, $two, $three);
}
或者更好的是,根本不使用原型。
此外,a
和 b
都是糟糕的名字。不要使用它们。
您为子程序提供的原型(从 Test::More::is
复制)表明您的子程序需要两个强制参数和一个可选参数。传入单个数组将不满足该原型 - 它被视为将在标量上下文中评估的单个参数。
解决方法是检索传递给子例程的两个(或三个)参数,并将它们分别传递给 Test::More::is
。
sub is ($$;$) {
my ($got, $expected, $test_name) = @_;
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is($got, $expected, $test_name);
}
问题与您使用原型或您正在重新定义子程序无关(严格来说,您不是因为两个子程序在不同的包中),但这是因为 Test::More::is()
有一个原型。
在 Perl 中,原型不会像改变解析规则那样验证参数。 $$;$
表示 sub 期望调用者匹配 is(EXPR, EXPR)
或 is(EXPR, EXPR, EXPR)
.
在这种情况下,绕过原型是最理想的。
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is(@_);
}
既然你不关心Test::More::is
是否修改了你的@_
,下面是一个简单的优化:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is;
}
如果 Test::More::is
使用 caller
,您会发现以下内容很有用:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
goto &Test::More::is;
}
当我重新定义自己的子例程(而不是 Perl 内置函数)时,如下所示:
perl -ce 'sub a($$$){} sub b {a(@_)}'
我收到此错误:
Not enough arguments for main::a at -e line 1, near "@_)"
我想知道为什么。
编辑:
"redefine"这个词可能选错了。但就我而言(我可能应该解释一下我最初尝试做的事情),我想通过在测试结果。
这是我所做的:
Test::More.pm :
sub is ($$;$) {
my $tb = Test::More->builder;
return $tb->is_eq(@_);
}
MyModule.pm :
sub is ($$;$) {
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is(@_);
}
你在这里没有重新定义任何东西。
你已经通过说 sub a($$$)
为你的子 a
设置了 prototype。函数定义中的美元符号告诉 Perl 这个 sub 恰好有三个标量参数。当你用 a(@_)
调用它时,Perl 不知道该列表中将有多少元素,因此它不知道调用将有多少参数,并在编译时失败。
不要乱用原型。你可能不需要它们。
相反,如果您知道您的子程序需要三个参数,请在您调用它的地方明确获取它们。
sub a($$$) {
...
}
sub b {
my ($one, $two, $three) = @_;
a($one, $two, $three);
}
或者更好的是,根本不使用原型。
此外,a
和 b
都是糟糕的名字。不要使用它们。
您为子程序提供的原型(从 Test::More::is
复制)表明您的子程序需要两个强制参数和一个可选参数。传入单个数组将不满足该原型 - 它被视为将在标量上下文中评估的单个参数。
解决方法是检索传递给子例程的两个(或三个)参数,并将它们分别传递给 Test::More::is
。
sub is ($$;$) {
my ($got, $expected, $test_name) = @_;
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is($got, $expected, $test_name);
}
问题与您使用原型或您正在重新定义子程序无关(严格来说,您不是因为两个子程序在不同的包中),但这是因为 Test::More::is()
有一个原型。
在 Perl 中,原型不会像改变解析规则那样验证参数。 $$;$
表示 sub 期望调用者匹配 is(EXPR, EXPR)
或 is(EXPR, EXPR, EXPR)
.
在这种情况下,绕过原型是最理想的。
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is(@_);
}
既然你不关心Test::More::is
是否修改了你的@_
,下面是一个简单的优化:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is;
}
如果 Test::More::is
使用 caller
,您会发现以下内容很有用:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
goto &Test::More::is;
}