混淆在将数组引用列表定义为常数时发生。

huangapple go评论58阅读模式
英文:

Confusion when defining a list of array refs as constant

问题

在一些Perl代码中,我正在进行一些低级编码,访问Perl常量,这些常量是列表,像这样:

use constant LIST => ()

由于Perl常量实际上是作为函数实现的,我正在尝试通过符号表以${__PACKAGE__.'::'}{LIST}->()的方式获取其值。

令人困惑的部分是,有时Perl会出现以下错误:

Not a CODE reference at ...

在运行调试器测试会话时,我有这样的印象,如果列表只有一个元素,常量会使用不同的表示方式:

  DB<18> use constant TEST => (['status', 1, undef])

  DB<19> x ${__PACKAGE__ . '::'}{TEST}
0  REF(0x2871fc8)
   -> ARRAY(0x2a914f0)
         0  'status'
         1  1
         2  undef
  DB<21> use constant TEST2 => (['status', 1, undef], ['something', 2, undef])

  DB<22> x ${__PACKAGE__ . '::'}{TEST2}
0  *Auth::Verifier::TEST2

有没有一种方法可以强制将常量变成函数?
我正在使用较旧的Perl(perl5(版本5.18.2的修订版))。

更新

似乎普通函数(与use constant相比)没有像https://stackoverflow.com/a/76407240/6607497建议的那种“优化”:

  DB<22> sub f3 { ([1]) }

  DB<23> sub f4 { ([1],[2]) }

  DB<24> x ${main::}{f3}->()
0  ARRAY(0x1413670)
   0  1

  DB<25> x ${main::}{f4}->()
0  ARRAY(0x1413508)
   0  1
1  ARRAY(0x13f9720)
   0  2

在这里,所有“常量”都是实际函数。
另请注意,添加()原型不会改变这一点(有关详细信息,请参阅“man perlsub”中的有关常量的部分)。

英文:

In some Perl code I'm doing some low-level coding accessing Perl constants that are lists, like this:

> use constant LIST => ()

As Perl constants are actually implemented as functions, I'm trying to get the value via the symbol table as ${__PACKAGE__.&#39;::&#39;}{LIST}-&gt;().

The confusing part is that sometimes Perl dies with:

> Not a CODE reference at ...

Running a debugger test session I got the impression that the constant uses a different representation if the list has only one element:

  DB&lt;18&gt; use constant TEST =&gt; ([&#39;status&#39;, 1, undef],)

  DB&lt;19&gt; x ${__PACKAGE__ . &#39;::&#39;}{TEST}
0  REF(0x2871fc8)
   -&gt; ARRAY(0x2a914f0)
         0  &#39;status&#39;
         1  1
         2  undef
  DB&lt;20&gt; x TEST
0  ARRAY(0x2a914f0)
   0  &#39;status&#39;
   1  1
   2  undef
  DB&lt;21&gt; use constant TEST2 =&gt; ([&#39;status&#39;, 1, undef], [&#39;something&#39;, 2, undef])

  DB&lt;22&gt; x ${__PACKAGE__ . &#39;::&#39;}{TEST2}
0  *Auth::Verifier::TEST2
  DB&lt;23&gt; x TEST2
0  ARRAY(0x2a092d8)
   0  &#39;status&#39;
   1  1
   2  undef
1  ARRAY(0x2a91808)
   0  &#39;something&#39;
   1  2
   2  undef
  DB&lt;24&gt; x ${__PACKAGE__ . &#39;::&#39;}{TEST2}-&gt;()
0  ARRAY(0x2a092d8)
   0  &#39;status&#39;
   1  1
   2  undef
1  ARRAY(0x2a91808)
   0  &#39;something&#39;
   1  2
   2  undef
  DB&lt;25&gt; x ${__PACKAGE__ . &#39;::&#39;}{TEST}-&gt;()
Not a CODE reference at (eval 35)[/usr/lib/perl5/5.18.2/perl5db.pl:732] line 2.
  DB&lt;26&gt;

Is there a way to force constants to be functions?
I'm using an older Perl (perl5 (revision 5 version 18 subversion 2)).

Update

It seems that regular functions (compared to use constant) doe not have such "optimization" that https://stackoverflow.com/a/76407240/6607497 suggests:

  DB&lt;22&gt; sub f3 { ([1]) }

  DB&lt;23&gt; sub f4 { ([1],[2]) }

  DB&lt;24&gt; x ${main::}{f3}-&gt;()
0  ARRAY(0x1413670)
   0  1
  DB&lt;25&gt; x ${main::}{f4}-&gt;()
0  ARRAY(0x1413508)
   0  1
1  ARRAY(0x13f9720)
   0  2
  DB&lt;26&gt; x ${main::}{f3}
0  *main::f3
  DB&lt;28&gt; x ${main::}{f4}
0  *main::f4

Here all "constants" are actual functions.
also note that adding a () prototype does not change that (see "man perlsub" on constants for details).

答案1

得分: 2

  • \&amp;TEST 将返回一个子程序。
  • &amp;TEST() 将调用它作为一个子程序。
  • 所以它们实际上是子程序。您只是在符号表中没有看到它们作为子程序。
  • 一段时间以前,符号表进行了优化以节省空间。我不知道所有的细节,但我相信与仅一个变量关联的符号会被替换为对该变量的引用。由于大多数条目只包含一个变量(通常是一个子程序),因此这样可以节省大量内存。访问符号表的代码需要考虑这些更改。
英文:
  • \&amp;TEST will return a sub.
  • &amp;TEST() will call it as a sub.

So they are effectively subs. You're simply not seeing them in the symbol table as subs.

A while back, the symbol table was optimized to save space. I don't know all the details, but I believe symbols associated with a only one variable are replaced with a reference to that variable. Since most entries contains only a single variable (usually a sub), this saves a lot of memory. Code visiting navigating the symbol table will need to take these changes into consideration.

答案2

得分: 0

以下是已翻译的内容:

在constant.pm中可以找到魔法发生的地方:

# ...
{
# ...
    if ($multiple || @_ == 1) {
        my $scalar = $multiple ? $constants->{$name} : $_[0];

# ...
        # 这个常量用于在5.8及更早版本中优化整个块的性能。
        if (_CAN_PCS && $symtab && !exists $symtab->{$name}) {
# ...
            Internals::SvREADONLY($scalar, 1);
            $symtab->{$name} = \$scalar;
            ++$flush_mro;
        } else {
            *$full_name = sub () { $scalar };
        }
    } elsif (@_) {
        my @list = @_;
        *$full_name = sub () { @list };
    } else {
        *$full_name = sub () { };
    }
}
# ...

所以,当只有一个单一值时,不会创建函数,而是使用该值的引用。

关于"将常量值的引用存储在符号表中等效于一个完整类型全局引用常量子例程,但节省了约400字节的内存"的原因在Constant subroutines 中有解释,该链接位于 metacpan.org 上,是Perl 5.8.9版本的更改之一。

然而,似乎"等效"意味着Perl解释器在内部处理了这种差异。

英文:

The magic what happens can be found in constant.pm:

# ...
        {
# ...
            if ($multiple || @_ == 1) {
                my $scalar = $multiple ? $constants-&gt;{$name} : $_[0];

# ...
                # The constant serves to optimise this entire block out on
                # 5.8 and earlier.
                if (_CAN_PCS &amp;&amp; $symtab &amp;&amp; !exists $symtab-&gt;{$name}) {
# ...
                    Internals::SvREADONLY($scalar, 1);
                    $symtab-&gt;{$name} = $scalar;
                    ++$flush_mro;
                } else {
                    *$full_name = sub () { $scalar };
                }
            } elsif (@_) {
                my @list = @_;
                *$full_name = sub () { @list };
            } else {
                *$full_name = sub () { };
            }
        }
# ...

So when there is only a single value, no function will be created, but the reference of the value will be used instead.

The reason ("Storing a reference to a constant value in a symbol table is equivalent to a full typeglob referencing a constant subroutine, but using about 400 bytes less memory.") is explained in Constant subroutines on metacpan.org as part of Perl 5.8.9 changes.

However it seems that "is equivalent" means the Perl interpreter handles the difference internally.

huangapple
  • 本文由 发表于 2023年6月5日 19:03:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76405794.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定