Perl:是否保证对常量进行单一计算?

发布于 2024-11-29 05:29:12 字数 960 浏览 0 评论 0原文

两天内我提出的第三个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夕嗳→ 2024-12-06 05:29:12

窥孔优化器将对常量 sub 的调用替换为该 sub 返回的值。在运行时 opttree 中没有对该子函数的任何调用。例如:

$ perl -MO=Concise -E\
  'sub answer () { 42 } for (1 .. 10) { say "The answer is ", answer }'
h  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 49 -e:1) v:%,2048 ->3
g     <2> leaveloop vK/2 ->h
7        <{> enteriter(next->d last->g redo->8) lKS/8 ->e
-           <0> ex-pushmark s ->3
-           <1> ex-list lK ->6
3              <0> pushmark s ->4
4              <
gt; const(IV 1) s ->5
5              <
gt; const(IV 10) s ->6
6           <
gt; gv(*_) s ->7
-        <1> null vK/1 ->g
f           <|> and(other->8) vK/1 ->g
e              <0> iter s ->f
-              <@> lineseq vK ->-
8                 <;> nextstate(main 48 -e:1) v:%,2048 ->9
c                 <@> say vK ->d
9                    <0> pushmark s ->a
a                    <
gt; const(PV "The answer is ") s ->b
b                    <
gt; const(IV 42) s ->c
d                 <0> unstack v ->e

这里感兴趣的一点是

c                 <@> say vK ->d
9                    <0> pushmark s ->a
a                    <
gt; const(PV "The answer is ") s ->b
b                    <
gt; const(IV 42) s ->c
d                 <0> unstack v ->e

,这表明 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:

$ perl -MO=Concise -E\
  'sub answer () { 42 } for (1 .. 10) { say "The answer is ", answer }'
h  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 49 -e:1) v:%,2048 ->3
g     <2> leaveloop vK/2 ->h
7        <{> enteriter(next->d last->g redo->8) lKS/8 ->e
-           <0> ex-pushmark s ->3
-           <1> ex-list lK ->6
3              <0> pushmark s ->4
4              <
gt; const(IV 1) s ->5
5              <
gt; const(IV 10) s ->6
6           <
gt; gv(*_) s ->7
-        <1> null vK/1 ->g
f           <|> and(other->8) vK/1 ->g
e              <0> iter s ->f
-              <@> lineseq vK ->-
8                 <;> nextstate(main 48 -e:1) v:%,2048 ->9
c                 <@> say vK ->d
9                    <0> pushmark s ->a
a                    <
gt; const(PV "The answer is ") s ->b
b                    <
gt; const(IV 42) s ->c
d                 <0> unstack v ->e

The bit of interest here is

c                 <@> say vK ->d
9                    <0> pushmark s ->a
a                    <
gt; const(PV "The answer is ") s ->b
b                    <
gt; const(IV 42) s ->c
d                 <0> unstack v ->e

Which shows that the arguments to the say operation are a const string "The answer is" and a const integer 42. There's no entersub opcode which would represent a sub call.

ら栖息 2024-12-06 05:29:12

的右侧

use constant PI => do { 4 * atan2 1, 1 };

只是一个表达式[1]。当执行语句时对其进行评估。由于该语句是use,因此它在编译时执行。由于人们通常不会将常量声明放入循环中,因此它只执行一次。[2]当编译移至下一行时,已定义以下子例程:

sub PI () { 3.14159265358979 }

这就是每个 useconstant 语句所发生的情况。右侧的表达式可以是任意复杂的,但由 constant 创建的子值将始终是常量编译时表达式的简单值。在文件的其余部分传播此常量值的机制是常量值子,但该子包含原始表达式的值,而不是表达式本身。

[1]:我从 http://perldoc.perl.org/perlsub.html 偷来的一个#Constant-Functions ——其中顺便包含了对 Perl 对内联子函数的作用的更完整的讨论。

[2]:如果它被执行多次,它会警告函数重定义——并且只有最后一次重定义会被保留。

The right-hand side of

use constant PI => do { 4 * atan2 1, 1 };

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:

sub PI () { 3.14159265358979 }

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 by constant 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.

淡紫姑娘! 2024-12-06 05:29:12

constant 编译指示是用于声明可内联子例程的语法糖。

当你这样写时:

use constant TEST => do {
    say 'Testing...';
    5;
};

与下面的完全等价:

BEGIN {
    require constant;
    constant->import(TEST => do {say 'Testing...'; 5});
}

你可以扩展 do 块,使执行顺序更清晰:

BEGIN {
    require constant;
    say 'Testing...';
    constant->import(TEST => 5);
}

由于 constant 只是语法糖,所以可以删除后,会产生以下代码:

BEGIN {
    say 'Testing...';
    *TEST = sub () {5};
}

因此,当perl编译代码时,它在编译时遇到BEGIN块并立即执行它。因此,在编译时会打印 Testing... 行,并声明子例程 TEST。由于 TEST 是使用 () 原型声明的(不带参数),并且其主体解析为单个常量值,因此该子例程是内联的候选例程。然后编译源代码的其余部分。

每当 Perl 在后续源代码中遇到对 TEST() 的调用时,它都会在编译时用 5 替换该调用。

The constant pragma is syntactic sugar for declaring inline-able subroutines.

When you write:

use constant TEST => do {
    say 'Testing...';
    5;
};

It is exactly equivalent to the following:

BEGIN {
    require constant;
    constant->import(TEST => do {say 'Testing...'; 5});
}

You can expand the do block to make the order of execution clearer:

BEGIN {
    require constant;
    say 'Testing...';
    constant->import(TEST => 5);
}

Since constant is just syntactic sugar, it can be removed, which leads to the following code:

BEGIN {
    say 'Testing...';
    *TEST = sub () {5};
}

So when perl compiles the code, it encounters the BEGIN block at compile time and immediately executes it. So at compile time the line Testing... is printed, and the subroutine TEST is declared. Since TEST 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 with 5 at compile time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文