为什么括号仅在子声明之后是可选的? [英] Why is parenthesis optional only after sub declaration?

查看:37
本文介绍了为什么括号仅在子声明之后是可选的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我正在探索 sub 的用法.

I am exploring the usage of sub.

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

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

This works as expected. The parenthesis is optional, like with many other functions, like print, open etc.

然而,这会导致编译错误:

However, this causes a compilation error:

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 @_; }

同理,一个不带参数的子,比如:

Similarly, a sub without args, such as:

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.

减轻这种特殊错误的方法是:

Ways to alleviate this particular error is:

  • 把&在子名称之前,例如&special_print
  • 在子名称后放置空括号,例如special_print()
  • 在脚本顶部使用 sub special_print 预先声明 special_print.
  • 在子声明之后调用special_print.
  • Put & before the sub name, e.g. &special_print
  • Put empty parenthesis after sub name, e.g. special_print()
  • Predeclare special_print with sub special_print at the top of the script.
  • Call special_print after the sub declaration.

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

My question is, why this special treatment? If I can use a sub globally within the script, why can't I use it any way I want it? Is there a logic to sub being implemented this way?

ETA:我知道如何修复它.我想知道这背后的逻辑.

ETA: I know how I can fix it. I want to know the logic behind this.

推荐答案

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

I think what you are missing is that Perl uses a strictly one-pass parser. It does not scan the file for subroutines, and then go back and compile the rest. Knowing this, the following describes how the one pass parse system works:

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

In Perl, the sub NAME syntax for declaring a subroutine is equivalent to the following:

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

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

This means that the sub NAME syntax has a compile time effect. When Perl is parsing source code, it is working with a current set of declarations. By default, the set is the builtin functions. Since Perl already knows about these, it lets you omit the parenthesis.

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

As soon as the compiler hits a BEGIN block, it compiles the inside of the block using the current rule set, and then immediately executes the block. If anything in that block changes the rule set (such as adding a subroutine to the current namespace), those new rules will be in effect for the remainder of the parse.

如果没有预先声明的规则,标识符将被解释如下:

Without a predeclared rule, an identifier will be interpreted as follows:

bareword       ===   'bareword'   # a string
bareword LIST  ===   syntax error, missing ','
bareword()     ===   &bareword()  # runtime execution of &bareword
&bareword      ===   &bareword    # same
&bareword()    ===   &bareword()  # same

如你所说使用strict和warnings时,barewords不会转成字符串,所以第一个例子是语法错误.

When using strict and warnings as you have stated, barewords will not be converted into strings, so the first example is a syntax error.

当预先声明有以下任何一项时:

When predeclared with any of the following:

sub bareword;
use subs 'bareword';
sub bareword {...}
BEGIN {*bareword = sub {...}}

那么标识符将被解释如下:

Then the identifier will be interpreted as follows:

bareword      ===   &bareword()     # compile time binding to &bareword
bareword LIST ===   &bareword(LIST) # same
bareword()    ===   &bareword()     # same
&bareword     ===   &bareword       # same
&bareword()   ===   &bareword()     # same

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

So in order for the first example to not be a syntax error, one of the preceding subroutine declarations must be seen first.

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

As to the why behind all of this, Perl has a lot of legacy. One of the goals in developing Perl was complete backwards compatibility. A script that works in Perl 1 still works in Perl 5. Because of this, it is not possible to change the rules surrounding bareword parsing.

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

That said, you will be hard pressed to find a language that is more flexible in the ways it lets you call subroutines. This allows you to find the method that works best for you. In my own code, if I need to call a subroutine before it has been declared, I usually use name(...), but if that subroutine has a prototype, I will call it as &name(...) (and you will get a warning "subroutine called too early to check prototype" if you don't call it this way).

这篇关于为什么括号仅在子声明之后是可选的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆