【问题标题】:Perl - anonymous hashmaps and arrays - few questionsPerl - 匿名哈希图和数组 - 几个问题
【发布时间】:2013-02-28 03:37:49
【问题描述】:

您好,我正在学习 perl,我将在这里发布几个假设。因此,如果我在某处有错误,请随时评论和纠正我。

  1. 创建哈希是通过以下方式完成的(在其他几种方法中):

    %numbers = qw(one 1 two 2);
    
  2. 创建数组的方法如下:

    @array = qw(one two);
    
  3. 以上结构表示“非匿名”类型。非匿名类型和匿名类型之间的主要区别在于命名类型有一个我可以参考的名称。如果我想创建匿名类型,我需要将括号 () 更改为数组中的方括号 [],或者更改为散列中的括号 {}。换句话说,散列的散列是对其他散列的引用的散列。因此我需要在嵌套散列中使用{},而不是经典散列()

    %HoH = (
        flintstones => {
            husband   => "fred",
            pal       => "barney",
        },
        jetsons => {
            husband   => "george",
            wife      => "jane",
            "his boy" => "elroy",  # quotes needed on key.
        },
        simpsons => {
            husband   => "homer",
            wife      => "marge",
            kid       => "bart",
        },
    );
    
  4. 同样的情况也适用于多维数组。多维数组是一个包含对另一个数组的引用的数组,因此需要使用[]代替()。

    @array_of_arrays =  ( [ "one", "two", "three" ],
                          [  4,   5,  6,  7  ],
                          [ "alpha", "beta" ]
                        );
    
  5. 如果我有包含每个家庭成员(flinstones、jetsons、simpsons)的“非匿名”哈希,我应该使用哪个构造来创建 %HOH

    $HOH{flinstones} = {%flinstones};
    

    $HOH{flinstones} = \%flinstones;
    

    我假设\%flinstones 只是将引用分配给$HOH{flinstones},这意味着我对%flinstones 所做的任何事情都会影响$HOH{flinstones},因为它只包含对它的引用。另一方面,{%flinstones} 类似于将“非匿名”哈希重新转换为“匿名”哈希。这样做的效果是%flinstones 可以稍后修改甚至删除,并且不会影响$HOH{flinstones},因为它引用了匿名哈希。

  6. 循环中的变量会发生什么?当my $variable; 在循环内发出时,它会覆盖旧的或创建新的,或者它是同一个变量,或者这里会发生什么?

    for($i=0;$i<4;$i++){
      my $variable=$i;
      print $variable
    }
    

【问题讨论】:

  • 每个问题只能问一个问题。

标签: arrays perl perl-data-structures


【解决方案1】:

至于问题 5,我假设是问题 1,你可以同时使用。虽然你应该意识到第一种方法:

$HOH{flinstones} = {%flinstones}

只是简单地制作%flinstones 哈希的浅表副本,在其中将其扩展为其键和值的列表。而

$HOH{flinstones} = \%flinstones

将散列作为引用传递,以便两个散列指向内存中的相同位置。

至于问题 6,词法作用域的变量会发生什么?来看看perldoc -f my

A "my" declares the listed variables to be local (lexically) to
the enclosing block, file, or "eval".

for 循环是一个块,这意味着在 for 循环中使用 my 声明的任何变量都是该循环的局部变量,并且该循环的每次迭代都是局部变量。这意味着如果你这样做:

for my $number (0 .. 3) {
    print "Number is $_. Last number was $last\n";
    my $last = $_;                       # WRONG!
}   # $last goes out of scope here!

它会给你很多Use of uninitialized value 警告。您需要扩大范围:

my $last = "N/A";  # default value
for my $number (0 .. 3) {
    print "Number is $_. Last number was $last\n";
    $last = $_;
}

现在,我不知道这是否是您故意的,但您可以将这两个问题合二为一:

my %HOH;
{ # begin a block to reduce scope of variables
    my %flinstones = (
        husband   => "fred",
        pal       => "barney",
    );
    $HOH{flinstones} = \%flinstones;
} 
... # %flinstones hash is now out of scope, stored only in %HOH

【讨论】:

    【解决方案2】:

    我称它们为“文字哈希”、“文字数组”,但对每个人来说都是他自己的。

    你应该知道,在 Perl 中——除了ties——[...]\@x 几乎是一回事。 {...}\%h 也是如此。它们都“构造”对数组和散列的引用。

    在问题 5 中,两者都会做你想做的事。 但是一个人会更有效地做到这一点。您的第二个示例将对已定义哈希的引用存储为另一个哈希中的值。第一个例子,

    $HOH{flinstones} = {%flinstones}
    

    根据列表上下文创建一个哈希以返回地址并将%flintstones展开到一个列表中。因此,它将作为%flintstones 的精确副本的散列存储在存储在%HOH 中的单独散列中。您是正确的,对%flintstones 的更改不会影响此副本。

    这里有一点建议给你。安装Smart::Comments (SC),创建一些测试脚本,然后通过 STDERR 转储变量内部。您会惊奇地发现,您可以通过观察一切事物的内部学到更多知识。

    以下是我在 SC 上的一些经验教训:

    • 如果您要转储 Win32::OLE 对象,请将 $Data::Dumper::Maxdepth 设置为某个正整数值,因为同一 OLE 对象的每个引用在遍历时可能看起来像不同的 Perl 对象。

    • 永远不要单独转储$_。由于某种原因,SC 中的代码可以更改它。所以总是做这样的事情:

      my $a = $_;
      ### $_ : $a
      
    • IO 句柄不会转储,所以不要尝试。使用默认字符串化。

    现在,最后,如果您没有将%flintstones%HOH 一起转储,您将无法通过简单的变量转储知道引用是否相同。但是,请记住您可以设置$Data::Dumper::Maxdepth,这样您就不会得到完整的转储。因此,您可以通过部分转储它们并使用直接的通用 Perl 引用字符串化来测试两个引用是否相同。

    ### %flintstones : '' . \%flintstones 
    local $Data::Dumper::Maxdepth = 1;
    ### %HOH
    

    亲自了解情况,这将帮助您更快地学习 Perl,而不是在 Stackoverflow 上提出大量问题。

    【讨论】:

      【解决方案3】:

      { LIST } 构造采用一个值列表,从中构建一个匿名散列(就像您使用 %hash = (LIST) 将同一列表分配给命名散列一样)并返回对该散列的引用。

      Perl 中的“匿名散列”没有什么特别之处:它们和其他散列一样都是普通的散列。使它们“匿名”的唯一原因是它们(当前)没有名称,因此您只能使用引用来引用它们。

      绑定到变量名也不是散列的固有属性:命名散列很可能变成匿名的(例如,如果它的名称超出范围),甚至获取匿名散列通过symbol table manipulation 命名,如下所示:

      my $hashref = {foo => 'bar'};
      our %hash;             # required by "use strict"
      *hash = $hashref;
      print "$hash{foo}\n";  # prints "bar"
      

      *hash = $hashref 行之后,全局变量%hash 成为引用$hashref 所指向的哈希的新名称,无论它之前是否已有名称。这种机制甚至允许同一个散列有多个名称:事实上,任何允许您将散列(或任何其他类型的变量)从其自己的名称空间导出到您的名称的 Perl 模块本质上就是在幕后做到这一点的。

      当然,以上所有内容也同样适用于数组(实际上也适用于标量)。


      至于你的最后一个问题,my 实际上每次执行时都会创建一个新的词法范围变量,而不是每次都重用同一个变量。这实际上对您的示例代码没有任何影响,但它产生影响的一种情况是,如果您在变量超出范围之前保存了对变量的引用。例如,以下是将制表符分隔的数据解析为数组的一种相当常见的方法:

      my @table;
      while (my $line = <>) {
          chomp $line;
          my @row = split /\t/, $line;
          # maybe do some manipulation or checks on @row here...
          push @table, \@row;
      }
      

      如果您对此进行测试,您会发现该代码确实在 @table 中填充了对每一行的不同(现在是匿名)数组的引用,而不是许多指向同一个数组的引用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-12
        • 2017-02-24
        相关资源
        最近更新 更多