Perl: 在Foreach循环中给数组元素分配引用

5

我本来想自己解决这个问题,但是我的脸现在因为不断地撞到这堵墙而疼痛。

我正在尝试加载9个文本文件,每个文件由一个7行7列字符矩阵组成,用空格分隔,然后将每个引用的矩阵保存到数组中的一个元素。我已经成功读取了每个文件,但当我去访问我的数组时,所有元素都相同。我一直在寻找解决方案,但要么我的问题没有得到答案,要么(更可能的是)我没有理解答案。以下是我代码中有问题的部分:

my @boardarray = (1, 2, 3, 4, 5, 6, 7, 8, 9);
sub LoadBoards {
    my (@board, $infile, @allboards);
    my $i = 1;
    @allboards = @boardarray;
    foreach (@allboards) {
        my $infile = "board" . $i . "\.brd";
        open FILE, "< $infile" or die $!;
        my $line = 0;
        while (<FILE>) {
            chomp $_;
            my @chars = split (/ /,$_);
            $board[$line] = [@chars];
            $line++;
        }
    my $tempboard = \@board;
        DisplayOneBoard($tempboard); print ("\n");              #Test A
    $boardarray[$i-1] = \@board;                                #Problem line?
        DisplayOneBoard($boardarray[$i-1]); print ("\n");       #Test B
        DisplayOneBoard($boardarray[0]); print ("\n----\n");    #Test C
    $i++;
    }
}

-我尝试将变量分配为@boardarray的元素,但没有改变。
-我在foreach循环中使用@boardarray,并将其更改为复制的@allboards,但没有改善。
我希望'Test A'和'Test B'行相同,并且'Test C'行保持我加载的第一个矩阵。 但是,每次迭代都是相同的三个矩阵。
(对于迭代1,它们都是矩阵1。 对于迭代2,它们都是矩阵2等)
最后,所有元素都是完全相同的矩阵(矩阵9)。

任何帮助都将不胜感激。谢谢。


我在这段代码中没有看到你更新$i的任何地方。 - mob
我在foreach循环的最底部执行$i++ - Royal Connell
2个回答

8
问题在于您在循环中每次都重复使用相同的 @board。当您将对该板子的引用推入 @boardarray 时,会每次推送一个指向相同 @board 的引用。解决方法很简单,只需将 my @board 移至您的 foreach 循环内部即可;这将在每次循环过程中创建一个新的 @board

4

如果您将代码分解得更细,并使用数组作为堆栈进行推入/弹出操作,那么您可能会有更好的运气:

sub load_file {
    my ($filename) = @_;
    open my $file, '<', $filename or die $!;
    my @array;
    while (<$file>) {
        chomp $_;
        my @chars = split (/ /,$_);
        push @array, \@chars;    ### adds a reference to the char line 
                                 ### array to the end of the array
    }
    return \@array;              ### return a ref to the 2-d array
}

sub load_files {
    my ($num) = @_;
    my %boards;     ### A hash, so we can refer to loaded arrays
                    ### with a string ID

    for my $filenum ( 1 .. $num ) {
         my $filename = "board" . $filenum . "\.brd";
         $boards{$filenum} = load_file($filename);
    }

    return \%boards;   ### return a ref to the hash of 2-d arrayrefs
}

### use it now...
my $boards = load_files(9); ### load 9 files.

DisplayOneBoard($boards->{6}); ### dereference our hashref, pass board in
                               ### key '6'to be displayed

谢谢。我不得不更改第一个子程序中的这一行:open FILE, '<', $filename or die $!;,以及第二个子程序中的这一行:my $filename = "board" . $filenum . "\.brd";,现在它完美地工作了。 - Royal Connell
谢谢 - 我已经纠正了代码。关于文件打开,最好的做法通常是使用词法文件句柄而不是全局变量(即标量),因为它可以在子程序之间传递,并且您还可以获得一些其他隐含的好处,例如当文件句柄超出作用域时自动关闭。因此,open $file ... 将适用于 while (<$file>) ... 读取操作。文件编号错误只是因为懒惰复制 :) - Oesor
我还注意到这会导致元素0为空。简单的更改也解决了这个问题。$boards{$filenum-1} = load_file($filename); - Royal Connell
对于文件句柄的良好建议。我也进行了相同的更改。我自学编程的书使用了glob,所以我一直都在这么做。 - Royal Connell
它确实会留下0的空位,但是使用$boards{$filenum-1} = load_file($filename)意味着$boards{'6'}将包含"board7.brd"的内容。如果你真的只想把boards当作一个数组来处理,那就声明为数组,并用push @boards, load_file($filename)加载它。 - Oesor
仅仅因为在我的代码中稍后我会将棋盘放置在一个网格上,这使得我可以更容易地引用位置数组元素和哈希键相同的数字,所以现在对我来说这很重要。虽然我可能会稍后改变它,因为我正在考虑随机化棋盘在网格上的布局。 - Royal Connell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接