为什么我的 Perl 函数原型 “(&;+)” 仍然需要 'sub'?

Why does my Perl function prototype of "(&;+)" still require 'sub'?

提问人:Joe Casadonte 提问时间:11/8/2023 最后编辑:Mark RotteveelJoe Casadonte 更新时间:11/23/2023 访问量:114

问:

我正在尝试使用 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

显然,我不明白发生了什么,因为这些在我看来都不一致。我显然错过了一些东西,但我不知道是什么。

Perl 函数原型

评论


答:

4赞 ikegami 11/8/2023 #1

&工作正常。

问题:

  • 块后有一个逗号 ()。=>
  • +是 的缩写,因此需要数组或哈希。\[@%]
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;
2赞 Shawn 11/8/2023 #2

从: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 };
0赞 brian d foy 11/9/2023 #3

有一些 Perl 内置函数可以将块作为它们的第一个参数。在这些情况下,块和下一个参数之间没有分隔字符(逗号):

my @transformed = map { ... } @input;
my @found = grep { ... } @input;

你的代码确实有逗号,所以事情是不同的:

with_some_context {...}, ...

由于 Perl 也使用大括号作为匿名哈希,因此 Perl 看到大括号中有第一件事,然后是大括号中的第二件事,并且必须决定它们是什么。由于块后面不应有逗号,因此它不能是子块。Perl 选择另一个选项,你会得到所有的语法错误。

评论

0赞 Joe Casadonte 11/9/2023
啊,我假设错误消息中的 hashref 指的是第二件事,但也许它指的是第一件事?而且,显然,我没有想过在这两件事之间放任何东西。
0赞 ikegami 11/9/2023
回复“有一些 Perl 内置函数可以将子程序作为它们的第一个参数”,是的,和。但不是和.goto &subexists &subdefined &submapgrep
0赞 brian d foy 11/14/2023
啊,是的,阻止。