提问人:Joe Casadonte 提问时间:11/8/2023 最后编辑:Mark RotteveelJoe Casadonte 更新时间:11/23/2023 访问量:114
为什么我的 Perl 函数原型 “(&;+)” 仍然需要 'sub'?
Why does my Perl function prototype of "(&;+)" still require 'sub'?
问:
我正在尝试使用 Perl 原型来了解有关它们的更多信息;我知道它们不像大多数其他语言那样工作。我不希望他们这样做。我特别希望获得一个包装函数来使用裸块作为代码引用,我认为这在以下两部分中有所暗示:perlsub
因为此功能的意图主要是让您定义 像内置函数一样工作的子例程
我的理解是你可以放弃 parens,并且
& 需要一个匿名子例程,如果作为第一个子例程传递 参数,不需要 sub 关键字或后面的逗号。
我还想将其他参数传递给包装器函数(而不是coderef)。这都是句法糖,但这就是我所追求的。
我有以下我认为可以工作的代码:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
sub with_some_context (&;+)
{
my($coderef, $context) = @_;
{
local %ENV = %ENV;
foreach my $key (keys %$context) {
$ENV{$key} = $context->{$key};
}
$coderef->();
}
}
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} => { shift(@ARGV) => 10 };
目的是让 Hashref 是函数的第二个参数,即 .编译器抱怨并且不使用 hashref 作为第二个参数:{ shift(@ARGV) => 10 }
with_some_context
$context
$ /tmp/foo SHLVL TERM SHLVL LANG
Useless use of anonymous hash ({}) in void context at /tmp/foo line 26.
SHLVL: 1
TERM: xterm-256color
SHLVL: 1
LANG: en_US.UTF-8
但是,如果我输入这个词,编译器会突然理解我的意图:sub
with_some_context sub {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} => { shift(@ARGV) => 10 };
正如我运行它时所证明的那样:
$ /tmp/foo SHLVL TERM SHLVL LANG
TERM: xterm-256color
SHLVL: 10
LANG: en_US.UTF-8
如果我删除 并在两个参数周围放置明确的 parens:sub
with_some_context({
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
}, { shift(@ARGV) => 10 });
编译器完全失去了它的狗屎:
$ /tmp/foo SHLVL TERM SHLVL LANG
"my" variable $key masks earlier declaration in same statement at /tmp/foo line 24.
"my" variable %ENV masks earlier declaration in same statement at /tmp/foo line 24.
"my" variable $key masks earlier declaration in same statement at /tmp/foo line 24.
"my" variable @ARGV masks earlier declaration in same statement at /tmp/foo line 26.
syntax error at /tmp/foo line 23, near "foreach "
Execution of /tmp/foo aborted due to compilation errors.
但是,如果我把背面放回去:sub
with_some_context(sub {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
}, { shift(@ARGV) => 10 });
或者去掉逗号,把括号放在 hashref 周围:
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} ({ shift(@ARGV) => 10 });
一切又开始工作了:
$ /tmp/foo SHLVL TERM SHLVL LANG
TERM: xterm-256color
SHLVL: 10
LANG: en_US.UTF-8
使用 ,不带 parens,也不带 hashref:sub
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
};
不会产生任何错误,但也不会做任何非常有用的事情:
$ /tmp/foo SHLVL TERM SHLVL LANG
SHLVL: 1
TERM: xterm-256color
SHLVL: 1
LANG: en_US.UTF-8
显然,我不明白发生了什么,因为这些在我看来都不一致。我显然错过了一些东西,但我不知道是什么。
答:
&
工作正常。
问题:
- 块后有一个逗号 ()。
=>
+
是 的缩写,因此需要数组或哈希。\[@%]
with_some_context { ... } %{ { shift(@ARGV) => 10 } };
没有理由接受数组。为什么哈希是可选的?
sub with_some_context(&\%) {
my ( $coderef, $context ) = @_;
local %ENV = ( %ENV, %$context );
return $coderef->();
}
with_some_context { ... } %{ { shift(@ARGV) => 10 } };
你真的想要求一个哈希吗?
sub with_some_context(&@) {
my $coderef = shift;
local %ENV = ( %ENV, @_ );
return $coderef->();
}
with_some_context { ... } shift( @ARGV ) => 10;
从:perlref
因为大括号(大括号)用于其他一些事情,包括 BLOCK,所以你有时可能不得不通过在前面加上一个“+”或“return”来消除语句开头的大括号的歧义,这样 Perl 就会意识到左大括号不是 BLOCK 的开头。使用卷发的经济性和记忆价值被认为值得这种偶尔的额外麻烦。
前导的效果与您尝试的哈希引用周围的括号相同。+
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} +{ shift(@ARGV) => 10 };
如果将哈希引用的键设置为字符串,它也可以工作;关于函数调用的某些内容(甚至是裸标量)使其解析为其他内容,从而导致错误。
这将失败:
my $first_arg = shift @ARGV;
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} { $first_arg => 10 };
但这有效
my $first_arg = shift @ARGV;
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} { "$first_arg" => 10 };
有一些 Perl 内置函数可以将块作为它们的第一个参数。在这些情况下,块和下一个参数之间没有分隔字符(逗号):
my @transformed = map { ... } @input;
my @found = grep { ... } @input;
你的代码确实有逗号,所以事情是不同的:
with_some_context {...}, ...
由于 Perl 也使用大括号作为匿名哈希,因此 Perl 看到大括号中有第一件事,然后是大括号中的第二件事,并且必须决定它们是什么。由于块后面不应有逗号,因此它不能是子块。Perl 选择另一个选项,你会得到所有的语法错误。
评论
goto &sub
exists &sub
defined &sub
map
grep
评论