Perl:是否保证对常量进行单一计算?
两天内我提出的第三个 Perl 问题。有些人会说我研究得不够努力,尽管我会说我正在帮助保持该部分的活跃性:P 不管怎样,我正在大声思考,希望能得到一个答案。
话不多说,让我引用 constant pragma 文档中的一句话:
当表达式中使用常量时,Perl 会将其替换为它的值 编译时的值,然后可以进一步优化表达式。
我只是想澄清一下:这是否意味着每次程序执行时所有表达式仅计算一次?因为它还说它们是使用内联子函数构建的,这让我印象深刻,因为它本质上是按使用评估的。我的意思是,如果你得到一个返回表达式的子程序,它会在每次调用时重新评估它,对吧?除非他们使用封闭变量或状态变量仅评估一次,但我不知道。
当然,我能保证这只评估一次吗?
#!/usr/bin/perl
use 5.014;
use autodie;
use strict;
use warnings;
use constant TEST => do {
say 'Testing...';
5;
};
say TEST foreach (1..4);
在这个特定的例子中,我似乎可以; “测试...”仅打印一次。但是我抛出的所有表达式都能保证这一点吗?
是啊,是啊,是啊。我应该在 CPAN 上使用 Readonly 。不幸的是,我的 Python 思想是,你应该尽可能坚持以标准方式做某事,因此我坚持使用老式的 constant 因为它是一个核心编译指示。
基本上,如果我将一个长而复杂的排序/grep/map 管道放入一个常量中,我可以 100% 保证仅进行一次评估吗?
Third Perl question from me in two days. Some will say I'm not researching hard enough, although I will say I'm helping keep the section active :P Either way, I'm pondering out loud in hope for an answer to be thrown my way.
Without further ado, let me quote a statement from the constant pragma's documentation:
When a constant is used in an expression, Perl replaces it with its
value at compile time, and may then optimize the expression further.
I just want to be clear: does this mean that all expressions are evaluated only once per program execution? Because it also says that they are built using inlined subs, which strike me as being inherently evaluate-per-usage. I mean, if you get a sub that returns an expression, it reevaluates it per call, right? Unless they're using enclosed variables or state variables to evaluate only once, but I don't know.
To be sure, can I guarantee this will only evaluate once?
#!/usr/bin/perl
use 5.014;
use autodie;
use strict;
use warnings;
use constant TEST => do {
say 'Testing...';
5;
};
say TEST foreach (1..4);
It seems in this particular example, I can; 'Testing...' is only printed once. But is this guaranteed of all expressions I throw at it?
Yeah, yeah, yeah. I should be using Readonly on CPAN. Unfortunately I come from the Python train of thought that you should stick to a standard way of doing something as much as you can, thus I'm sticking with the antiqued constant because it's a core pragma.
Basically, if I throw a long complex sort/grep/map pipeline into a constant, can I 100% guarantee only a single evaluation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
窥孔优化器将对常量 sub 的调用替换为该 sub 返回的值。在运行时 opttree 中没有对该子函数的任何调用。例如:
这里感兴趣的一点是
,这表明
say
操作的参数是一个 const 字符串“答案是”和一个 const 整数 42。没有entersub
代表子调用的操作码。The peephole optimizer replaces calls to constant subs with the value returned by that sub. In the runtime optree there isn't any call to that sub. For illustration:
The bit of interest here is
Which shows that the arguments to the
say
operation are a const string "The answer is" and a const integer 42. There's noentersub
opcode which would represent a sub call.的右侧
只是一个表达式[1]。当执行语句时对其进行评估。由于该语句是
use
,因此它在编译时执行。由于人们通常不会将常量声明放入循环中,因此它只执行一次。[2]当编译移至下一行时,已定义以下子例程:这就是每个
useconstant
语句所发生的情况。右侧的表达式可以是任意复杂的,但由constant
创建的子值将始终是常量编译时表达式的简单值。在文件的其余部分传播此常量值的机制是常量值子,但该子包含原始表达式的值,而不是表达式本身。[1]:我从 http://perldoc.perl.org/perlsub.html 偷来的一个#Constant-Functions ——其中顺便包含了对 Perl 对内联子函数的作用的更完整的讨论。
[2]:如果它被执行多次,它会警告函数重定义——并且只有最后一次重定义会被保留。
The right-hand side of
is merely an expression[1]. It is evaluated when the statement is executed. Since the statement is a
use
, it is executed at compile time. Since people don't usually put constant declarations inside loops, it is only executed once.[2] By the time compilation moves to the next line, the following subroutine has been defined:This is what happens with every
use constant
statement. The expression on the right-hand side can be arbitrarily complex, but the value of the sub created byconstant
will always be the simple value of the expression at the time the constant was compiled. The mechanism for propagating this constant value throughout the rest of the file is a constant-valued sub, but that sub contains the value of the original expression, not the expression itself.[1]: One I stole from http://perldoc.perl.org/perlsub.html#Constant-Functions -- which incidentally contains a more complete discussion of what Perl does with inlined subs.
[2]: And if it is executed more than once, it warns about the function redefinition -- and only the last redefinition sticks.
constant
编译指示是用于声明可内联子例程的语法糖。当你这样写时:
与下面的完全等价:
你可以扩展
do
块,使执行顺序更清晰:由于
constant
只是语法糖,所以可以删除后,会产生以下代码:因此,当perl编译代码时,它在编译时遇到
BEGIN
块并立即执行它。因此,在编译时会打印Testing...
行,并声明子例程TEST
。由于TEST
是使用()
原型声明的(不带参数),并且其主体解析为单个常量值,因此该子例程是内联的候选例程。然后编译源代码的其余部分。每当 Perl 在后续源代码中遇到对
TEST()
的调用时,它都会在编译时用5
替换该调用。The
constant
pragma is syntactic sugar for declaring inline-able subroutines.When you write:
It is exactly equivalent to the following:
You can expand the
do
block to make the order of execution clearer:Since
constant
is just syntactic sugar, it can be removed, which leads to the following code:So when perl compiles the code, it encounters the
BEGIN
block at compile time and immediately executes it. So at compile time the lineTesting...
is printed, and the subroutineTEST
is declared. SinceTEST
is declared with a()
prototype (takes no arguments) and its body resolves to a single constant value, the subroutine is a candidate for inlining. The rest of the source is then compiled.Whenever perl encounters a call to
TEST()
in the subsequent source code, it will replace that call with5
at compile time.