Perl - 匿名哈希图和几个问题



你好,我正在学习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. 如果我有包含每个家庭成员(打火石、杰森、辛普森(的"非匿名"哈希,我应该使用哪种结构来创建%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
    }
    

至于问题 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 $lastn";
    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 $lastn";
    $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

我称它们为"文字哈希"、"文字数组",但每个人都有自己的。

你应该知道,在 Perl 中——除了tie的情况——[...]@x几乎是一回事。{...}%h也是如此。它们都"构造"对数组和哈希的引用。

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

$HOH{flinstones} = {%flinstones}

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

这里有一些建议给你。安装,Smart::Comments(SC(,创建一些测试脚本,然后通过STDERR转储变量内部。你会惊讶地发现,看到一切的内部结构,你能学到多少东西,你想看到。

以下是我在SC方面的经验中的一些教训:

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

  • 切勿自行倾倒$_。出于某种原因,SC 中的代码可以更改它。所以总是做这样的事情:

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

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

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

亲眼看看这个案例是什么,将帮助你更快地学习Perl,而不是在Stackoverflow上提出大量问题。

{ LIST }构造获取值列表,从中构建匿名哈希(就像您将相同的列表分配给带有 %hash = (LIST) 的命名哈希一样(并返回对该哈希的引用。

Perl 中的"匿名哈希"没有什么特别之处:它们和其他任何哈希一样都是普通的哈希。 唯一使它们"匿名"的是它们(目前(没有名称,因此您只能使用引用来引用它们。

绑定到变量名称也不是哈希的固有属性:命名哈希很有可能变得匿名(例如,如果它的名称超出范围(,甚至匿名哈希通过符号表操作获取名称,如下所示:

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,而不是用许多指向同一数组的引用填充。

最新更新