开发者

Perl: Is Single Evaluation on Constants Guaranteed?

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 thou开发者_StackOverflow社区ght 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?


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              <$> const(IV 1) s ->5
5              <$> const(IV 10) s ->6
6           <$> 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                    <$> const(PV "The answer is ") s ->b
b                    <$> 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                    <$> const(PV "The answer is ") s ->b
b                    <$> 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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜