为什么在Perl列表中压缩引用?

时间:2010-06-17 23:25:06

标签: regex perl reference

将预编译的正则表达式放在列表中引用的两个不同的哈希值中:

my @list = ();

my $regex = qr/ABC/;

push @list, { 'one' => $regex };
push @list, { 'two' => $regex };

use Data::Dumper;
print Dumper(\@list);

我希望:

$VAR1 = [
      {
        'one' => qr/(?-xism:ABC)/
      },
      {
        'two' => qr/(?-xism:ABC)/
      }
    ];

但我们得到一个循环引用:

$VAR1 = [
      {
        'one' => qr/(?-xism:ABC)/
      },
      {
        'two' => $VAR1->[0]{'one'}
      }
    ];

无限期嵌套的哈希引用和浅层复制$regex会发生这种情况。

我假设基本原因是预编译的正则表达式实际上是引用,并且同一列表结构中的引用被压缩为优化(\ $标量的行为方式相同)。我没有完全看到这样做的效用(可能是对引用的引用具有相同的内存占用),但也许基于内部表示的原因

这是正确的行为吗?我可以阻止它发生吗?除了可能使GC更加困难之外,这些圆形结构会产生非常严重的问题。例如,迭代可能包含相同正则表达式的查询列表会使MongoDB驱动程序崩溃,并使用令人讨厌的段错误(请参阅https://rt.cpan.org/Public/Bug/Display.html?id=58500

3 个答案:

答案 0 :(得分:9)

这是预期的行为。

您的引用并非真正的通告;你有两个单独的项目,指向同一件事。 Data :: Dumper在内存中打印人类可读的Perl可解析的数据结构表示,它的真正含义是$list[0]->{one}$list[1]->{two}都指向同一个东西。

Perl使用引用计数垃圾收集,虽然它可能会遇到循环数据结构的问题,但这种数据结构没有特别的问题。

答案 1 :(得分:6)

这里没有什么好笑的事。

  1. 您在同一数据结构中存储了两次相同的引用。
  2. 然后你要求Data :: Dumper打印该结构的表示。
  3. Data :: Dumper希望尽可能忠实地往返于您提供的数据,这意味着它需要输出Perl代码,该代码将生成一个数据结构,该数据结构包含与$list[0]{one}相同的引用。 $list[0]{two}
  4. 它通过输出数据结构来实现这一点,其中一个成员包含对同一结构的另一个成员的引用。
  5. 但它实际上并不是循环参考。

答案 2 :(得分:1)

  我假设基本原因是这样的   实际上是预编译的正则表达式   引用和内部的引用   相同的列表结构被压缩为   优化(\ $标量表现为   同样的方式)。我没有完全看到   这样做的效用(大概是一个   对引用的引用也是一样的   内存足迹),但也许有一个   基于内部的原因   表示

原因是它可能是对嵌套在某处的数据结构的引用,其中包含一个返回顶层(循环)的引用。如果它继续进入这样的结构,那么它将创建一个无限循环。它避免这种情况的方法是永远不会递归到它已经看过的参考文献中,所以它会打印它已经看过它并引导你到之前的打印位置。

在这种情况下,没有循环,但是Data :: Dumper无法知道在它进入结构之前就已经太晚了。

对于这样的标量,这可能不是必要的,但可能会发生,因为Data :: Dumper会在检查类型之前检查它是否已经看过引用。它还提供了一个好处,它表明它是对相同数据的引用,而不是它的副本,这可能是有用的信息,如果它只打印了值就会丢失。