首页 > 解决方案 > 在 perl 中声明匿名数组集

问题描述

我正在尝试为 suduko 中的每个单元格聚合生成数组。我似乎已经解决了这个问题,但不明白我之前的替代方案在做什么。

例如,如果我写的话,我会得到我期望的答案:

@row = ( [], [], [], [], [], [], [], [], [] ) ;

我曾预料到

@row = ( [] ) x 9 ;

表现得一样。我也试过了,效果更好

@row = ( [] x 9 ) ;

在两个数组中,只有第一个元素对此感到奇怪。对于第一个被拒绝的表单,我得到每个数组中的所有 81 个元素

我确实想知道最后一种形式是否真的合法?

# prob.pl - Show problem with repeat anonymous arrays
#
# ###################################################

@row = ( [] x 9 ) ;
@col = ( [] x 9 ) ;
@box = ( [] x 9 ) ;

for ( $i = 0 ; $i < 81 ; $i ++ ) {
   push( @{$row[ row($i) ]}, $i ) ;
   push( @{$col[ col($i) ]}, $i ) ;
   push( @{$box[ box($i) ]}, $i ) ;
}

for ( $i = 0 ; $i < 9 ; $i ++ ) {
   print STDERR "\@{\$row[$i]} = @{$row[$i]}\n" ;
   print STDERR "\@{\$col[$i]} = @{$col[$i]}\n" ;
   print STDERR "\@{\$box[$i]} = @{$box[$i]}\n" ;
}

sub row {
   my( $i ) = @_ ;

   int( $i / 9 ) ;
}

sub col {
   my( $i ) = @_ ;

   $i % 9 ;
}

sub box {
   my( $i ) = @_ ;

   int( col( $i ) / 3 ) + 3 * int( row( $i ) / 3 ) ;
}

标签: perl

解决方案


分两部分回答:第一部分是对正在发生的事情的简单看法以及您应该采取哪些措施来解决它。第二部分试图解释你得到的这种奇怪的行为。


第 1 部分 - 简单说明

所有形式都是合法的;它们只是不等价的,并且可能不符合您的期望。在这种情况下,Data::Dumper或者Data::Printer是您的朋友:

use Data::Printer;
my @a1 = ( [] x 9 );
p @1;

打印类似的东西

[
    [0] "ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)"
]

引用x(“重复运算符”,如果您需要搜索它)的文档:

在标量上下文中,或者如果左操作数既没有括在括号中也没有 qw// 列表,它执行字符串重复。

(在标量上下文中[] x 9调用)x

另一方面,当你这样做时([]) x 9,你会得到类似的东西:

[
    [0] [],
    [1] var[0],
    [2] var[0],
    [3] var[0],
    [4] var[0],
    [5] var[0],
    [6] var[0],
    [7] var[0],
    [8] var[0]
]

再次引用文档:

如果 x 在列表上下文中,并且左操作数被括在括号中或 qw// 列表中,则它执行列表重复。在这种情况下,它为左操作数提供列表上下文,并返回一个列表,该列表由左操作数列表重复右操作数指定的次数组成。

这里发生的事情[]是在应用之前进行评估x。它创建一个arrayref,x 9然后将其复制9 次。

实现您想要的正确方法将是您的第一个解决方案,或者,如果您更喜欢更简洁(但仍然可读)的东西:

my @row = map { [] } 1 .. 9;

(因为地图的主体在每次迭代时都会被评估,它确实创建了 9 个不同的引用)

或者,您可以不进行初始化@row@col@box让 autovivification 在需要时创建数组。


第 2 部分 - 高级解释

当您使用([] x 9). 为简单起见,让我用一个更简单的例子来重现它:

use feature 'say';

@x = ([] x 5);
@y = ([] x 5);
@z = ([] x 5);

push @{$x[0]}, 1;
push @{$y[0]}, 1;
push @{$z[0]}, 1;

say "@{$x[0]}";
say "@{$y[0]}";
say "@{$z[0]}";

该程序输出:

1 1
1
1 1

有趣的是,从这个程序中删除@y( @y = ([] x 5)) 的定义会产生输出:

1
1
1

一些可疑的事情正在发生。我用两点来解释。

首先,让我们考虑以下示例:

use Data::Printer;
use feature 'say';

say "Before:";
@x = "abcd";
p @x;
say "@{$x[0]}";

say "After:";
push @{$x[0]}, 5;
p @x;
say "@{$x[0]}";
say $abcd[0];

哪个输出

Before:
[
    [0] "abcd"
]

After:
[
    [0] "abcd"
]
5
5

当我们这样做时push @{$x[0]}, 5@{$x[0]}就变成了@{"abcd"},它创建了数组@abcd,并推5入其中。$x[0]仍然是一个字符串 ( abcd),但这个字符串也是一个数组的名称。

其次*,我们看下面的程序:

use Data::Printer;

@x = ([] x 5);
@y = ([] x 5);
@z = ([] x 5);

p @x;
p @y;
p @z;

我们得到输出:

[
    [0] "ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)"
]
[
    [0] "ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)"
]
[
    [0] "ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)"
]

@x@z包含相同的参考。虽然这令人惊讶,但我认为这是可以解释的:第 1 行,[] x 5创建了一个 arrayref,然后将其转换为一个字符串 to do x 5,然后它不再使用 arrayref。这意味着垃圾收集器可以自由地回收它的内存,而 Perl 可以自由地在这个地址重新分配其他东西。出于某种原因,这不会立即发生(@y不包含与 相同的内容@x),而是仅在分配@z. 这可能只是垃圾收集器/优化器实现的结果,我怀疑它可能会从一个版本更改为另一个版本。

最后,发生的事情是这样的:@x并且@z包含一个相同的元素,一个字符串。当您取消引用$x[0]and$z[0]时,您将获得相同的数组。因此,推入$x[0]$z[0]推入同一个数组。

这会被 捕获use strict,它会说:

Can't use string ("ARRAY(0x2339f30)ARRAY(0x2339f30)"...) as an ARRAY ref while "strict refs" in use at repl1.pl line 11.

*请注意,对于第二部分,我不确定这会发生什么,这只是我的(有点受过教育的)猜测。请不要相信我的话,如果您知道得更好,请随时纠正我。


推荐阅读