2015-06-05 69 views
2

我正在编写一个脚本,用于分析文件并希望将整个结果存储在数组中。我跑成我归结于以下代码中的问题:将Perl数组放入散列表

use strict; 
use warnings; 

use Data::Dumper; 

my @sections =(); 
my @sections1 = qw/sect1 sect2 sect3/; 
my @sections2 = qw/sect4 sect5/; 

my @textbooks =(); 

my $text1 = { title => "title1", author => "author1" }; 
my $text2 = { title => "title2", author => "author2" }; 

for (@sections1) { 
    push(@sections, $_); 
} 
$text1->{sections} = \@sections; 
@sections =(); 

for (@sections2) { 
    push(@sections, $_); 
} 

push(@textbooks, $text1); 
$text2->{sections} = \@sections; 
print Dumper($text2); 

push(@textbooks, $text2); 

print Dumper(@textbooks); 

第一打印自卸车的结果如下:

$VAR1 = { 
      'title' => 'title2', 
      'sections' => [ 
          'sect4', 
          'sect5' 
         ], 
      'author' => 'author2' 
     } 

,第二个的结果为:

$VAR1 = { 
      'sections' => [ 
          'sect4', 
          'sect5' 
         ], 
      'title' => 'title1', 
      'author' => 'author1' 
     }; 
$VAR2 = { 
      'title' => 'title2', 
      'sections' => $VAR1->{'sections'}, 
      'author' => 'author2' 
     }; 

我对此有两个问题,我确信它们是相关的。

  • 我意识到第一部分是一个引用,以便它们包含我期望的第二部分。我认为行@sections=()会创建一个新的数组,并在稍后进行新的引用。显然我没有正确地想到这一点。

  • 第二部分是怎么回事?从$text2的输出看来,它正确无误,直到推送。

回答

1

你是完全正确的:

@sections =(); 

确实创建一个新的阵列,它覆盖旧的内容。

Data::Dumper'hides'之一是参考文献。

试试这个:

foreach my $key (keys %{$text2}) { 
    print $key, $text2 -> {$key},"\n" 
} 

和:

foreach my $thing (@textbooks) { 
    print "TB ref $thing\n"; 
    foreach my $key (keys %{$thing}) { 
     print "$key => ", $thing->{$key}, "\n"; 
    } 
} 

从后者,你会得到这样的:

TB ref HASH(0x12de994) 
sections => ARRAY(0x12e43b4) 
author => author1 
title => title1 
TB ref HASH(0x12de9ac) 
sections => ARRAY(0x12e43b4) 
title => title2 
author => author2 

注意数组如何在这里有相同的ID - 这是你问题的根源。你的散列有两次相同的引用。(你的哈希的数组中的父hashes不同)

一个简单的解决办法可能是使用@sections切换到$sections,因为同时:

@sections =(); 

不使一个“新”的数据结构:

$sections = []; 

将因为[]是一个匿名数组,您正在创建,只是重新分配引用。

另一种解决方案是通常将范围缩小为my,但您的代码不能很好地工作 - 但与foreach循环很好地配合。

+0

使用'@sections = [];'正是我所需要的。谢谢。 –

1

\@sections是容器(数组)的参考@sections

@sections =()不创建新数组,但清空旧数组。要检查两个变量是否相同,比较它们的引用,

use strict; 
use warnings; 

my @sections = 1 .. 3; 
my $ref1 = \@sections; 
@sections =(); 

print "identical!\n" if $ref1 == \@sections; 
+0

本地使用'my'作为最小代码块的范围可以很好地减少这类问题。正如使用anon数组ref。 – Sobrique

+0

@Sobrique是的,这取决于你想达到什么。 –

1

的问题是,只有一个@sections阵列,无论你做它的内容是什么,参考\@sections将指向同一个阵列

的防振输出显示你这个

$VAR2 = { 
      'title' => 'title2', 
      'sections' => $VAR1->{'sections'}, 
      'author' => 'author2' 
     }; 

是说,$VAR2->{sections}是同一基准$VAR1->{sections},因为他们都是一个和借鉴,这显然是真实的e到相同的@sections阵列。它包含的数据仅仅是最新修改后剩下的数据。这是@sections2副本,将包含('sect4', 'sect5')

一些概念,可以帮助被

  • 你应该声明变量作为晚越好,最好在使用他们的第一点

  • 您可以编写循环

    for (@sections1) { 
        push(@sections, $_); 
    } 
    

    刚刚

    push @sections, @sections1 
    
  • 而不是

    $text1->{sections} = \@sections 
    

    其分配给数组@sections一个参考,你可以使用

    $text1->{sections} = [ @sections ] 
    

    这将创建一个新的,匿名数组,从内容填充它@sections,并返回对其的引用。这使得你可以自由地修改@sections不改变已保存在$text1

你的代码也许应该写这样的数据的副本,但是你有没有说你想要做的任何事情与数据,因此您可能需要一些更改

我强烈建议您使用Data::Dump而不是Data::Dumper。您可能需要安装它,因为它不是一个核心模块,但它是值得的,因为转储输出更整洁,更简洁,易读

use strict; 
use warnings; 

use Data::Dump; 

my @sections1 = qw/ sect1 sect2 sect3 /; 
my @sections2 = qw/ sect4 sect5 /; 

my @textbooks = (
    { title => 'title1', author => 'author1' }, 
    { title => 'title2', author => 'author2' }, 
); 

$textbooks[0]{sections} = \@sections1; 
$textbooks[1]{sections} = \@sections2; 

dd \@textbooks; 

输出

[ 
    { 
    author => "author1", 
    sections => ["sect1", "sect2", "sect3"], 
    title => "title1", 
    }, 
    { 
    author => "author2", 
    sections => ["sect4", "sect5"], 
    title => "title2", 
    }, 
] 
+0

我知道仅仅使用\ @ sections1和\ @ sections2会给我想要的结果,但是我使用了一个循环,并且数组是从文件中填充的。匿名数组[]很好。 –

+0

@PeterStaab:如果你是从一个文件定义数组,那么 - 正如我在我的解决方案中评论的那样 - 你正在用错误的范围声明它们。如果他们在填充和使用它们的循环中被声明*,那么你可以简单地引用你最初的引用。这比用匿名数组复制信息更好,但是没有看到更大的图片,我无法帮助您详细了解 – Borodin