perl - 为什么括号只有在子声明之后才可选?

标签 perl

(假设 use strict; use warnings; 在整个问题中。)

我正在探索 sub 的用法.

sub bb { print @_; }
bb 'a';

这按预期工作。括号是可选的,与许多其他函数一样,如 print, open等等

但是,这会导致编译错误:
bb 'a';
sub bb { print @_; }

String found where operator expected at t13.pl line 4, near "bb 'a'"
        (Do you need to predeclare bb?)
syntax error at t13.pl line 4, near "bb 'a'"
Execution of t13.pl aborted due to compilation errors.

但这不会:
bb('a');
sub bb { print @_; }

同样,一个不带args的sub,比如:
special_print;
my special_print { print $some_stuff }

会导致这个错误:
Bareword "special_print" not allowed while "strict subs" in use at t13.pl line 6.
Execution of t13.pl aborted due to compilation errors.

减轻此特定错误的方法是:
  • 将 & 放在子名称之前,例如&special_print
  • 在子名称后放置空括号,例如special_print()
  • 预先声明 special_printsub special_print在脚本的顶部。
  • 调用 special_print在子声明之后。

  • 我的问题是,为什么要进行这种特殊处理?如果我可以在脚本中全局使用 sub,为什么我不能以任何我想要的方式使用它? sub 有逻辑吗?以这种方式实现?

    ETA:我知道如何解决它。我想知道这背后的逻辑。

    最佳答案

    我认为您缺少的是 Perl 使用严格的一次性解析器。它不会扫描文件中的子例程,然后返回并编译其余部分。知道了这一点,下面描述了一次解析系统是如何工作的:

    在 Perl 中,sub NAME声明子例程的语法等价于以下内容:

    sub name {...}   ===   BEGIN {*name = sub {...}}
    

    这意味着 sub NAME语法具有编译时效果。当 Perl 解析源代码时,它使用的是当前的一组声明。默认情况下,该集合是内置函数。由于 Perl 已经知道这些,它可以让你省略括号。

    一旦编译器遇到 BEGIN block ,它就会使用当前规则集编译 block 的内部,然后立即执行该 block 。如果该 block 中的任何内容更改了规则集(例如将子例程添加到当前 namespace ),则这些新规则将对解析的其余部分生效。

    如果没有预先声明的规则,标识符将被解释如下:
    bareword       ===   'bareword'   # a string
    bareword LIST  ===   syntax error, missing ','
    bareword()     ===   &bareword()  # runtime execution of &bareword
    &bareword      ===   &bareword    # same
    &bareword()    ===   &bareword()  # same
    

    如您所说,使用严格和警告时,裸词不会转换为字符串,因此第一个示例是语法错误。

    当预先声明以下任何一项时:
    sub bareword;
    use subs 'bareword';
    sub bareword {...}
    BEGIN {*bareword = sub {...}}
    

    那么标识符将被解释如下:
    bareword      ===   &bareword()     # compile time binding to &bareword
    bareword LIST ===   &bareword(LIST) # same
    bareword()    ===   &bareword()     # same
    &bareword     ===   &bareword       # same
    &bareword()   ===   &bareword()     # same
    

    因此,为了使第一个示例不是语法错误,必须首先看到前面的子例程声明之一。

    至于这一切背后的原因,Perl 有很多遗产。开发 Perl 的目标之一是完全向后兼容。在 Perl 1 中运行的脚本仍然在 Perl 5 中运行。因此,无法更改围绕裸字解析的规则。

    也就是说,您将很难找到一种在调用子例程方面更灵活的语言。这使您可以找到最适合您的方法。在我自己的代码中,如果我需要在子程序被声明之前调用它,我通常使用 name(...) ,但如果该子程序有原型(prototype),我将其称为 &name(...) (如果你不这样调用它,你会得到一个警告“子程序调用太早而无法检查原型(prototype)”)。

    关于perl - 为什么括号只有在子声明之后才可选?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5992715/

    相关文章:

    perl - 使用 Perl Text::CSV 从数据库写入

    perl - 为什么我在比较 Perl 中输入的行时遇到问题?

    mysql - Perl 和 DBD::mysql 无法加载 mysql.so...也许所需的共享库或 dll 没有安装在预期的位置

    file - 在 perl 中,如何生成文件中包含的所有可能的数字组合?

    perl - 有没有办法使用 Net::SFTP::Foreign 递归地列出文件?

    perl - 如何正确关闭 Bot::BasicBot 机器人(基于 POE::Component::IRC)?

    linux - perl 解压单个文件

    perl - 在字符串中搜索递归模式

    linux - 逐行读取文件 fork/exec - Perl

    perl - Dancer2::Plugin::Redis中 'retry'和 'every'配置参数的意义是什么