【问题标题】:Initializing hash of arrays初始化数组的哈希
【发布时间】:2021-02-06 08:05:18
【问题描述】:

我想要一个计数器数组的哈希值,初始值为 0。 这是我对代码的第一次尝试:

my @names = ("", a, b, c);
my %hsh = ();
for $i (1..3)
{
 # I expected this line to give me a fresh new array each time - it did not.
 $hsh{$names[$i]} = (0, 0, 0);  # Assignment line #
 doprint($i); #see below
}
#output is: ARRAY(0x1501128) ARRAY(0x1501128) ARRAY(0x1501128)

我们看到它们都是同一个数组,这是不希望的。 为了解决这个问题,我将 #Assignment 行更改为:

 $hsh{$names[$i]} = ( );
 push(@{$hash{$names[$i]}}, 0, 0, 0);
 # output is: ARRAY(0x1510c70) ARRAY(0x1510d18) ARRAY(0x1510dc0)

现在都是不同的数组,生活还不错。

如果赋值行使用 (0, 0, $i) 我会得到不同的数组(至少在这里 - 它们可能会在其他地方重用)
但我无法通过使用 (0, 0, $i - $i) 或更复杂的公式来生成 0 值来愚弄它。

这是 Perl 的某种优化吗?为什么 (0, 0, 0) 不是每次都是一个新数组? (0, 0) 或 (0, 0, 0, 0) 会不同吗?

为了完整起见,这里是打印子程序:

sub doprint
{
 my $i = shift;
 print (\@{$hsh{$names[$i]}}, " ");
}

---- 以下编辑是在“没有数组注释”之后的 2 月 9 日上午 - 我想添加一些代码,而 cmets 不允许这样做。

好的:$h{$i} = (0, 0, 0);将其初始化为标量值 0。
但是,如下面的输出所示,某处有一个数组。
编写 $h{$i}[$i] 是否在标量旁边创建了 @-something?

my %h = ();
sub dotest
{
 my $desc = shift;
 print "    # $desc\n";
 for (0, 1, 2)
 {
  $h{$_}[$_] += 1 + $_;
 }

 for (0, 1, 2)
 {
  print "    # $_: $h{$_} ";
  print "# @{$h{$_}}" . " # $h{$_}[$_] \n";
 }
 print "\n";
}

%h = ( );
$h{0} = [0, 0, 0];
$h{1} = [0, 0, 0];
$h{2} = [0, 0, 0];
dotest("Initialize to [0, 0, 0]");

%h = ( );
$h{0} = (0, 0, 0);
$h{1} = (0, 0, 0);
$h{2} = (0, 0, 0);
dotest("Initialize to (0,0,0)");

%h = ( );
dotest("No initialization");

%h = ( );
$h{0} = \@{(0, 0, 0)};
$h{1} = \@{(0, 0, 0)};
$h{2} = \@{(0, 0, 0)};
dotest("Initialize to \\\@{(0, 0, 0)}");


# Initialize to [0, 0, 0] - Separate arrays, initialized to zeros.
# 0: ARRAY(0x10ca5e0) # 1 0 0 # 1 
# 1: ARRAY(0x10ca778) # 0 2 0 # 2 
# 2: ARRAY(0x2709da0) # 0 0 3 # 3 

# Initialize to (0,0,0) - scalars AND a shared array?
# 0: 0 # 1 2 3 # 1 
# 1: 0 # 1 2 3 # 2 
# 2: 0 # 1 2 3 # 3 

# No initialization  - separate arrays, initialized to null
# 0: ARRAY(0x2709e48) # 1 # 1 
# 1: ARRAY(0x10ca718) #  2 # 2 
# 2: ARRAY(0x2709da0) #   3 # 3 

# Initialize to \@{(0, 0, 0)} - reusing the shared array from previous?
# 0: ARRAY(0x2709d88) # 2 4 6 # 2 
# 1: ARRAY(0x2709d88) # 2 4 6 # 4 
# 2: ARRAY(0x2709d88) # 2 4 6 # 6 

【问题讨论】:

    标签: arrays perl perl-hash


    【解决方案1】:

    Dave Cross has already showed you如何做你想做的事。但是,这里有血淋淋的细节。

    对标量的赋值是在标量上下文中,而对散列的单元素访问是标量。所以,这是在标量上下文中:

    $hash{key} = ( 0, 0, 0 );
    

    赋值右侧的运算符是逗号。在标量上下文中,comma operator 评估其左侧,然后丢弃结果。它评估右侧并返回。请参阅perlfaq4,我也对此进行了解释。这是 C 中的一个特性,并出现在其他几种语言中。

    当值不特殊时更容易看出:

    $hash{key} = ( 9, 56, 137 );
    

    所以,开始在标量上下文中评估右侧:

    $hash{key} = (   9, 56,   137 );
    $hash{key} = ( ( 9, 56 ), 137 );
    
    $hash{key} = ( 56, 137 );
    
    $hash{key} = ( 137 );
    

    您最终会得到最右边的评估结果。就是这样:

    $hash{key} = 137;
    

    有些人 (ab) 使用它来组合两个必须在不同行中的语句(实际上不是在 Perl 中,而是在其他人中,maybe JavaScript):

    my $foo = (  ($sideeffect=$a), ($b = $c) );
    

    或者,您可以在通常只使用一个的地方组合表达式(尽管 who 是一个表达式):

    for( my $i = 0; $i < 10; $j++, $i++ ) { ... }
    

    现在,问题的第二部分。您有该键的非引用值,然后您尝试取消引用它以使用 push 中的数组。那是行不通的,因为它从来都不是参考。

    【讨论】:

    • 我用各种方式初始化$hash{$key},然后做$hash{$key}[$i]++来计数。使用 [list of values] 或 push(list of values),我得到每个键的正确初始化的值列表。如果我什么都不做,或者 $hash{$key} = (),每个键都有自己的数组,未初始化。所以我留下了这个问题:为什么 $hash{$key} = (0, 0, 0) 导致所有键似乎共享同一个数组? ( $hash{"a"}[1]++; $hash{"b"}[1]++; 将为 $hash{"a"} 和 $hash{"b" 的下标 1 打印 2 }.)
    • 没有数组,正如我所解释的和 Dave Cross 所展示的。他们没有分享任何东西。
    • 请查看原始问题的大编辑。谢谢
    • 你最初做的事情没有数组。阅读我的答案。做戴夫克罗斯告诉你的。在生活中继续前进。您更改为的其余内容与原始问题无关,并且在您已经得到答案之后将问题从我们下面更改出来是一种糟糕的形式。如果您有新问题,请提出新问题。其余的只是让以后阅读本文的其他人感到困惑。
    【解决方案2】:

    这行不符合你的想法:

    $hsh{$names[$i]} = (0, 0, 0);
    

    (0, 0, 0) 不是数组,而是列表。而且您不能将数组(或者实际上是列表)存储在哈希元素中。您只能将标量值存储在哈希中。

    您可以通过将 reference 存储到哈希中的数组来解决此问题。最简单的方法是使用匿名数组构造函数 ([ ... ])。

    $hsh{$names[$i]} = [0, 0, 0];
    

    【讨论】:

    • 谢谢...将 (0, 0, 0) 更改为 [0, 0, 0] 解决了问题,但我还是有点困惑。你能解释一下当我将值设置为 (0,0,0)、vs (0,0,1) 和 ( ) 然后将列表推到上面时存储在哈希中的内容吗?
    • @TG:这就是Data::Dumper 的用途:-)
    • 嗯,真的更糟。它甚至不是一个列表。它是标量上下文中的逗号运算符。它评估其左侧并丢弃结果,然后评估右侧并返回它。当项目不寻常时更容易看到,例如 ( 4, 9, 137 )。人们通常认为您得到了元素的数量,因为他们使用 (1, 2, 3) 进行测试,而 3 恰好是元素的数量以及最后一项。
    猜你喜欢
    • 1970-01-01
    • 2015-06-08
    • 1970-01-01
    • 2016-10-24
    • 2012-03-20
    • 2016-02-10
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    相关资源
    最近更新 更多