我正在探索sub的用法。
sub bb { print @_; } bb 'a';
这按预期工作。括号是可选的,与许多其他函数一样,如打印,打开等。
但是,这会导致编译错误:
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的子例如:
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()
> Predeclare special_print with sub special_print在脚本的顶部。
>在子声明之后调用special_print。
我的问题是,为什么这种特殊治疗?如果我可以在脚本中全局使用一个sub,为什么我不能使用它任何方式我想要它?有没有一个逻辑,sub子实现这种方式?
ETA:我知道我该如何解决它。我想知道这背后的逻辑。
解决方法
在Perl中,用于声明子例程的子NAME语法等效于以下内容:
sub name {...} === BEGIN {*name = sub {...}}
这意味着子NAME语法具有编译时效果。当Perl解析源代码时,它使用当前的一组声明。默认情况下,集合是内置函数。由于Perl已经知道这些,它允许你省略括号。
一旦编译器命中一个BEGIN块,它就使用当前规则集编译块的内部,然后立即执行该块。如果该块中的任何内容更改规则集(例如向当前命名空间添加子例程),那么这些新规则将对解析的其余部分有效。
没有预先声明的规则,标识符将被解释如下:
bareword === 'bareword' # a string bareword LIST === Syntax error,missing ',' bareword() === &bareword() # runtime execution of &bareword &bareword === &bareword # same &bareword() === &bareword() # same
当使用严格和警告,如你所说,barewords不会转换成字符串,所以第一个例子是语法错误。
当用以下任何一种预先声明:
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(…),但是如果该子程序有一个原型,我将它称为& name(…)你会得到一个警告“子程序调用太早检查原型”,如果你不这样调用)。