这是命令 perldsc,可以使用我们的多个免费在线工作站之一在 OnWorks 免费托管服务提供商中运行,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器
程序:
您的姓名
perldsc - Perl 数据结构手册
商品描述
Perl 让我们拥有复杂的数据结构。 你可以写这样的东西和所有的
突然之间,您将拥有一个具有三个维度的数组!
对于我的 $x (1 .. 10) {
对于我的 $y (1 .. 10) {
对于我的 $z (1 .. 10) {
$迎角[$x][$y][$z] =
$x ** $y + $z;
}
}
}
唉,不管这看起来多么简单,在它下面是一个比
满足眼睛!
你怎么打印出来? 为什么不能只说“打印@AoA”? 你如何排序? 如何
你能把它传递给一个函数或从一个函数中取回其中之一吗? 它是一个对象吗?
您可以将其保存到磁盘以供稍后阅读吗? 您如何访问整行或整列
那个矩阵? 所有的值都必须是数字吗?
如您所见,很容易混淆。 虽然有一小部分原因是
这可以归因于基于参考的实现,这实际上更多是由于
缺乏为初学者设计的示例的现有文档。
本文档旨在详细但易于理解的处理许多不同的
您可能想要开发的各种数据结构。 它也应该作为一本食谱
例子。 这样,当您需要创建这些复杂的数据结构之一时,您可以
只需从这里捏、窃或窃取一个插入示例即可。
让我们详细看看这些可能的结构中的每一个。 有单独的部分
以下各项:
· 数组数组
· 数组的散列
· 散列数组
· 散列的散列
· 更精细的构造
但是现在,让我们看看所有这些类型的数据结构共有的一般问题。
参考文献:
了解 Perl 中所有数据结构的最重要的事情——包括
多维数组——即使它们可能以其他方式出现,Perl @ARRAYs 和
%HASH 内部都是一维的。 它们只能保存标量值(意味着
字符串、数字或引用)。 它们不能直接包含其他数组或散列,但是
而是包含 引用 到其他数组或散列。
您不能像使用真正的对象那样使用对数组或散列的引用
数组或散列。 对于不习惯区分数组和数组的 C 或 C++ 程序员
指向相同的指针,这可能会令人困惑。 如果是这样,只需将其视为差异
在结构和指向结构的指针之间。
您可以(并且应该)阅读有关 perlref 中引用的更多信息。 简而言之,参考文献是
更像是知道它们指向什么的指针。 (对象也是一种引用,
但我们不会马上需要它们——如果有的话。)这意味着当你有
在您看来就像访问二维或多维数组和/或
hash,真正发生的事情是基本类型只是一个一维实体
包含对下一级的引用。 只是你可以 使用 它好像是一个
二维的。 这实际上是几乎所有 C 多维数组的工作方式
以及。
$array[7][12] # 数组数组
$array[7]{string} # 哈希数组
$hash{string}[7] # 数组的散列
$hash{string}{'another string'} # 散列的散列
现在,因为顶层只包含引用,如果你尝试打印出你的数组
一个简单的 打印() 功能,你会得到一些看起来不太好的东西,比如
这个:
我的@AoA = ( [2, 3], [4, 5, 7], [0] );
打印 $AoA[1][2];
7
打印@AoA;
ARRAY(0x83c38)ARRAY(0x8b194)ARRAY(0x8b1d0)
那是因为 Perl 不会(永远)隐式地取消引用您的变量。 如果你想
得到一个参考所指的东西,然后你必须自己使用
前缀输入指示符,如“${$blah}”、“@{$blah}”、“@{$blah[$i]}”,或者其他
后缀指针箭头,如“$a->[3]”、“$h->{fred}”,甚至“$ob->method()->[3]”。
COMMON 错误
构造数组之类的东西时最常犯的两个错误是
要么不小心计算了元素的数量,要么引用了相同的元素
内存位置重复。 在这种情况下,您只获得计数而不是
嵌套数组:
对于我的 $i (1..10) {
我的@array = somefunc($i);
$AoA[$i] = @array; # 错误的!
}
这只是将数组分配给标量并获取其元素的简单情况
数数。 如果那是你真正想要的,那么你最好考虑成为
更明确一点,像这样:
对于我的 $i (1..10) {
我的@array = somefunc($i);
$counts[$i] = 标量@array;
}
这是一次又一次引用同一内存位置的情况:
# 要么不严格,要么有外部作用域 my @array;
# 宣言。
对于我的 $i (1..10) {
@array = somefunc($i);
$AoA[$i] = \@array; # 错误的!
}
那么,这有什么大问题呢? 看起来不错,不是吗? 毕竟,我只是告诉
你知道你需要一系列的引用,所以天哪,你让我一个!
不幸的是,虽然这是真的,但它仍然被破坏了。 @AoA 中的所有引用均指
此 非常 同 地方,因此它们都将保存@array 中最后的内容! 它是
类似于以下 C 程序中演示的问题:
#包括
主要的() {
结构密码 *getpwnam(), *rp, *dp;
rp = getpwnam("root");
dp = getpwnam("守护进程");
printf("守护进程名称为 %s\nroot 名称为 %s\n",
dp->pw_name, rp->pw_name);
}
哪个会打印
守护进程名称是守护进程
根名称是守护进程
问题是“rp”和“dp”都是指向内存中同一个位置的指针! 在 C 中,
你必须记住 malloc() 自己一些新的记忆。 在 Perl 中,你会想要使用
数组构造函数“[]”或哈希构造函数“{}”代替。 这是正确的方法
做前面损坏的代码片段:
# 要么不严格,要么有外部作用域 my @array;
# 宣言。
对于我的 $i (1..10) {
@array = somefunc($i);
$AoA[$i] = [@array];
}
方括号引用一个新数组 复制 @array 中的内容
任务的时间。 这就是你想要的。
请注意,这将产生类似的内容,但阅读起来要困难得多:
# 要么不严格,要么有外部作用域 my @array;
# 宣言。
对于我的 $i (1..10) {
@array = 0 .. $i;
@{$AoA[$i]} = @array;
}
是一样的吗? 好吧,也许是——也许不是。 细微的差别是,当你
在方括号中分配一些东西,你肯定知道它总是一个全新的参考
用一个新的 复制 的数据。 在这个新案例中可能还有其他事情发生
“@{$AoA[$i]}”在赋值的左侧取消引用。 这一切都取决于
$AoA[$i] 开始时是否未定义,或者它是否已经包含一个
参考。 如果您已经用引用填充@AoA,如
$AoA[3] = \@another_array;
然后在左侧的间接赋值将使用现有的
已经存在的参考:
@{$AoA[3]} = @数组;
当然啦 将 具有破坏@another_array 的“有趣”效果。 (有
你有没有注意到当程序员说某事“有趣”时,而不是
意思是“有趣”,令人不安的是,它们更倾向于表示“令人讨厌”,
“困难”,还是两者兼而有之? :-)
所以请记住始终使用带有“[]”或“{}”的数组或散列构造函数,并且
你会没事的,虽然它并不总是最佳效率。
令人惊讶的是,以下看起来很危险的构造实际上可以正常工作:
对于我的 $i (1..10) {
我的@array = somefunc($i);
$AoA[$i] = \@array;
}
那是因为 我的() 与其说是编译时声明,不如说是运行时声明
为 se。 这意味着 我的() 每次通过循环重新创建变量。
所以即使它 容貌 好像每次都存储相同的变量引用
其实没有! 这是一个微妙的区别,可以在
除了最有经验的程序员之外,还有误导所有人的风险。 所以我通常建议
反对教给初学者。 事实上,除了向函数传递参数,我
很少看到在代码中大量使用 gimme-a-reference 运算符(反斜杠)。
相反,我建议初学者他们(以及我们大多数人)应该尝试使用
更容易理解的构造函数“[]”和“{}”,而不是依赖词法(或
动态)范围和隐藏的引用计数在幕后做正确的事情。
总结:
$AoA[$i] = [@array]; # 通常最好
$AoA[$i] = \@array; # 危险的; my() 是怎样的数组?
@{ $AoA[$i] } = @array; # 对大多数程序员来说太棘手了
警告 ON 优先级
说起“@{$AoA[$i]}”这样的东西,其实下面是同一个东西:
$aref->[2][2] # 清除
$$aref[2][2] # 令人困惑
那是因为 Perl 对其五个前缀解引用器的优先级规则(看起来像
有人发誓:“$ @ * % &”)使它们比后缀下标绑定得更紧密
括号或大括号! 这无疑会给 C 或 C++ 程序员带来巨大的冲击,
谁非常习惯使用 *a[i] 来表示 我是 的元素
“一种”。 也就是说,他们首先取下标,然后才取消引用那个东西
下标。 这在 C 中很好,但这不是 C。
Perl 中看似等效的构造,$$aref[$i] 首先执行 $aref 的 deref,
使其将 $aref 作为对数组的引用,然后取消引用它,最后
告诉你 我是 $AoA 指向的数组的值。 如果你想要 C 的概念,你会
必须写“${$AoA[$i]}”来强制 $AoA[$i] 在
领先的“$”解引用器。
为什么 您 应该 ALWAYS “用 严格的”
如果这听起来比它的价值更可怕,请放松。 Perl 有一些特性
帮助您避免最常见的陷阱。 避免混淆的最好方法是
像这样启动每个程序:
#!/usr/bin/perl -w
用严格;
这样,您将被迫声明所有变量 我的() 并且也不允许
意外的“符号解引用”。 因此,如果你这样做了:
我的 $aref = [
[ "fred", "barney", "pebbles", "bambam", "dino", ],
[ "homer", "bart", "marge", "maggie", ],
[“乔治”、“简”、“埃尔罗伊”、“朱迪”、]、
];
打印 $aref[2][2];
编译器会立即将其标记为错误 at 编 次,因为你是
不小心访问了@aref,一个未声明的变量,因此它会提醒你
改写:
打印 $aref->[2][2]
调试
您可以使用调试器的“x”命令来转储复杂的数据结构。 例如,
鉴于上述 $AoA 的分配,这是调试器输出:
DB<1> x $AoA
$AOA = ARRAY(0x13b5a0)
0 ARRAY(0x1f0a24)
0 '弗雷德'
1 '巴尼'
2个“鹅卵石”
3 '班巴姆'
4 '恐龙'
1 ARRAY(0x13b558)
0 '本垒打'
1 '巴特'
2 '玛格'
3 '玛姬'
2 ARRAY(0x13b540)
0 '乔治'
1 '简'
2 'elroy'
3 '朱迪'
守则 示例
几乎没有评论(这些将在某一天获得自己的联机帮助页)这里很短
说明访问各种类型数据结构的代码示例。
阵列 OF 阵列
声明 of an ARRAY OF 阵列
@AoA = (
[“弗雷德”,“巴尼”],
[“乔治”,“简”,“埃尔罗伊”],
[“本垒打”、“玛吉”、“巴特”]、
);
代 of an ARRAY OF 阵列
# 从文件中读取
而 ( <> ) {
推@AoA, [分裂];
}
# 调用函数
对于 $i ( 1 .. 10 ) {
$AoA[$i] = [ somefunc($i) ];
}
# 使用临时变量
对于 $i ( 1 .. 10 ) {
@tmp = somefunc($i);
$AoA[$i] = [@tmp];
}
# 添加到现有行
推@{ $AoA[0] }, "wilma", "betty";
使用权 和 印字 of an ARRAY OF 阵列
# 一个元素
$AoA[0][0] = "弗雷德";
# 另一个元素
$AoA[1][1] =~ s/(\w)/\u$1/;
# 使用 refs 打印整个内容
对于 $aref ( @AoA ) {
打印 "\t [ @$aref ],\n";
}
# 用索引打印整个东西
对于 $i ( 0 .. $#AoA ) {
打印 "\t [ @{$AoA[$i]} ],\n";
}
# 一次打印整个内容
对于 $i ( 0 .. $#AoA ) {
对于 $j ( 0 .. $#{ $AoA[$i] } ) {
打印 "elt $i $j 是 $AoA[$i][$j]\n";
}
}
哈希值 OF 阵列
声明 of a HASH OF 阵列
%HoA = (
燧石 => [“弗雷德”,“巴尼”],
jetsons => [“乔治”,“简”,“埃尔罗伊”],
辛普森一家 => [“本垒打”、“玛吉”、“巴特”]、
);
代 of a HASH OF 阵列
# 从文件中读取
#燧石:弗雷德·巴尼·威尔玛·迪诺
而 ( <> ) {
下一个除非 s/^(.*?):\s*//;
$HoA{$1} = [拆分];
}
# 从文件中读取; 更多临时工
#燧石:弗雷德·巴尼·威尔玛·迪诺
while ( $line = <> ) {
($who, $rest) = 拆分 /:\s*/, $line, 2;
@fields = split ' ', $rest;
$HoA{$who} = [@fields];
}
# 调用返回列表的函数
对于 $group ( "simpsons", "jetsons", "flintstones" ) {
$HoA{$group} = [ get_family($group) ];
}
# 同样,但使用临时值
对于 $group ( "simpsons", "jetsons", "flintstones" ) {
@members = get_family($group);
$HoA{$group} = [@members];
}
# 将新成员追加到现有家族
推@{ $HoA{"flintstones"} }, "wilma", "betty";
使用权 和 印字 of a HASH OF 阵列
# 一个元素
$HoA{flintstones}[0] = "Fred";
# 另一个元素
$HoA{辛普森一家}[1] =~ s/(\w)/\u$1/;
# 打印整个内容
foreach $family ( 键 %HoA ) {
打印 "$family: @{ $HoA{$family} }\n"
}
# 用索引打印整个东西
foreach $family ( 键 %HoA ) {
打印“家庭:”;
foreach $i ( 0 .. $#{ $HoA{$family} } ) {
打印“ $i = $HoA{$family}[$i]”;
}
打印“\n”;
}
# 打印按成员数量排序的整个内容
foreach $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } 键 %HoA ) {
打印 "$family: @{ $HoA{$family} }\n"
}
# 打印按成员数量和名称排序的整个内容
foreach $family(排序{
@{$HoA{$b}} <=> @{$HoA{$a}}
||
$cmp $b
} 键 %HoA )
{
打印 "$family: ", join(", ", sort @{ $HoA{$family} }), "\n";
}
阵列 OF 哈希值
声明 of an ARRAY OF 哈希值
@AoH = (
{
铅 => "弗雷德",
朋友 => "barney",
},
{
铅 => "乔治",
妻子 => "简",
儿子 => "elroy",
},
{
铅 => "本垒打",
妻子 => "marge",
儿子 => "bart",
}
);
代 of an ARRAY OF 哈希值
# 从文件中读取
# 格式:LEAD=fred FRIEND=barney
而 ( <> ) {
$rec = {};
对于 $field ( split ) {
($key, $value) = 拆分 /=/, $field;
$rec->{$key} = $value;
}
推@AoH, $rec;
}
# 从文件中读取
# 格式:LEAD=fred FRIEND=barney
#没有温度
而 ( <> ) {
推@AoH,{ 拆分 /[\s+=]/ };
}
# 调用一个返回键/值对列表的函数,比如
# "lead","fred","daughter","pebbles"
而 ( %fields = getnextpairset() ) {
推@AoH, { %fields };
}
# 同样,但不使用临时变量
而 (<>) {
推@AoH, { parsepairs($_) };
}
# 给元素添加键/值
$AoH[0]{pet} = "恐龙";
$AoH[2]{pet} = "圣诞老人的小帮手";
使用权 和 印字 of an ARRAY OF 哈希值
# 一个元素
$AoH[0]{lead} = "fred";
# 另一个元素
$AoH[1]{铅} =~ s/(\w)/\u$1/;
# 使用 refs 打印整个内容
对于 $href (@AoH) {
打印 ”{ ”;
对于 $role ( 键 %$href ) {
打印“$role=$href->{$role}”;
}
打印“}\n”;
}
# 用索引打印整个东西
对于 $i ( 0 .. $#AoH ) {
打印 "$i 是 { ";
对于 $role ( 键 %{ $AoH[$i] } ) {
打印“$role=$AoH[$i]{$role}”;
}
打印“}\n”;
}
# 一次打印整个内容
对于 $i ( 0 .. $#AoH ) {
对于 $role ( 键 %{ $AoH[$i] } ) {
打印 "elt $i $role 是 $AoH[$i]{$role}\n";
}
}
哈希值 OF 哈希值
声明 of a HASH OF 哈希值
%HoH = (
打火石 => {
铅 => "弗雷德",
朋友 => "巴尼",
},
杰森斯 => {
铅 => "乔治",
妻子 => "简",
"他的男孩" => "elroy",
},
辛普森一家 => {
铅 => “本垒打”,
妻子 => "marge",
孩子 => "巴特",
},
);
代 of a HASH OF 哈希值
# 从文件中读取
# 燧石:lead=fred pal=barney 妻子=wilma pet=dino
而 ( <> ) {
下一个除非 s/^(.*?):\s*//;
$谁 = $1;
对于 $field ( split ) {
($key, $value) = 拆分 /=/, $field;
$HoH{$who}{$key} = $value;
}
# 从文件中读取; 更多临时工
而 ( <> ) {
下一个除非 s/^(.*?):\s*//;
$谁 = $1;
$rec = {};
$HoH{$who} = $rec;
对于 $field ( split ) {
($key, $value) = 拆分 /=/, $field;
$rec->{$key} = $value;
}
}
# 调用一个返回键值散列的函数
对于 $group ( "simpsons", "jetsons", "flintstones" ) {
$HoH{$group} = { get_family($group) };
}
# 同样,但使用临时值
对于 $group ( "simpsons", "jetsons", "flintstones" ) {
%members = get_family($group);
$HoH{$group} = { %members };
}
# 将新成员追加到现有家族
%new_folks = (
妻子 => "威尔玛",
宠物 => "恐龙",
);
对于 $what (keys %new_folks) {
$HoH{打火石}{$what} = $new_folks{$what};
}
使用权 和 印字 of a HASH OF 哈希值
# 一个元素
$HoH{flintstones}{wife} = "wilma";
# 另一个元素
$HoH{辛普森一家}{lead} =~ s/(\w)/\u$1/;
# 打印整个内容
foreach $family ( 键 %HoH ) {
打印“$family:{”;
对于 $role ( 键 %{ $HoH{$family} } ) {
打印“$role=$HoH{$family}{$role}”;
}
打印“}\n”;
}
# 打印整个事情有点排序
foreach $family ( 排序键 %HoH ) {
打印“$family:{”;
对于 $role ( 排序键 %{ $HoH{$family} } ) {
打印“$role=$HoH{$family}{$role}”;
}
打印“}\n”;
}
# 打印按成员数量排序的整个内容
foreach $family ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$a}} }
键 %HoH )
{
打印“$family:{”;
对于 $role ( 排序键 %{ $HoH{$family} } ) {
打印“$role=$HoH{$family}{$role}”;
}
打印“}\n”;
}
# 为每个角色建立一个排序顺序(rank)
$ i = 0;
for ( qw(主妻儿子女儿朋友宠物) ) { $rank{$_} = ++$i }
# 现在打印按成员数量排序的整个内容
foreach $family ( sort { keys %{ $HoH{$b} } <=> keys %{ $HoH{$a} } }
键 %HoH )
{
打印“$family:{”;
# 并根据排名顺序打印这些
对于 $role ( sort { $rank{$a} <=> $rank{$b} }
键 %{ $HoH{$family} } )
{
打印“$role=$HoH{$family}{$role}”;
}
打印“}\n”;
}
了解更多 精心制作的 记录
声明 of 了解更多 精心制作的 记录
这是一个示例,展示了如何创建和使用具有许多不同字段的记录
种类:
$rec = {
文本 => $string,
序列 => [@old_values],
查找 => { %some_table },
THATCODE => \&some_function,
此代码 => sub { $_[0] ** $_[1] },
句柄 => \*标准输出,
};
打印 $rec->{TEXT};
打印 $rec->{SEQUENCE}[0];
$last = pop @ { $rec->{SEQUENCE} };
打印 $rec->{LOOKUP}{"key"};
($first_k, $first_v) = 每一个 %{ $rec->{LOOKUP} };
$answer = $rec->{THATCODE}->($arg);
$answer = $rec->{THISCODE}->($arg1, $arg2);
# 注意 fh ref 上的额外块括号
打印 { $rec->{HANDLE} } "一个字符串\n";
使用文件句柄;
$rec->{HANDLE}->自动冲洗(1);
$rec->{HANDLE}->print(" 一个字符串\n");
声明 of a HASH OF 复杂 记录
%电视 = (
打火石 => {
系列 => "打火石",
晚上 => [ qw(星期一星期四星期五)],
成员 => [
{ 姓名 => "fred", 角色 => "lead", 年龄 => 36, },
{ name => "wilma", role => "wife", age => 31, },
{ name => "pebbles", role => "kid", age => 4, },
],
},
杰森斯 => {
系列 => "jetsons",
晚上 => [ qw(星期三星期六)],
成员 => [
{ name => "george", role => "lead", age => 41, },
{ name => "jane", role => "wife", age => 39, },
{ name => "elroy", role => "kid", age => 9, },
],
},
辛普森一家 => {
系列 =>“辛普森一家”,
晚上 => [ qw(周一) ],
成员 => [
{ name => "homer", role => "lead", age => 34, },
{ name => "marge", role => "wife", age => 37, },
{ name => "bart", role => "kid", age => 11, },
],
},
);
代 of a HASH OF 复杂 记录
# 从文件中读取
# 这最容易通过让文件本身来完成
# 在原始数据格式中,如上所示。 perl 很开心
# 解析复杂的数据结构,如果声明为数据,所以
# 有时这样做是最容易的
# 这里是一块一块的构建
$rec = {};
$rec->{series} = "燧石";
$rec->{nights} = [ find_days() ];
@成员 = ();
# 假设这个文件在 field=value 语法中
而 (<>) {
%fields = 拆分 /[\s=]+/;
推@members, { %fields };
}
$rec->{members} = [@members];
# 现在记住整件事
$TV{ $rec->{series} } = $rec;
################################################ #########
# 现在,你可能想要创建有趣的额外字段
# 将指针包含回相同的数据结构中,因此如果
# 改变一件,它随处改变,例如
# 如果你想要一个作为参考的 {kids} 字段
# 到一组孩子的记录而没有重复
# 记录并因此更新问题。
################################################ #########
foreach $family(键 %TV){
$rec = $TV{$family}; #临时指针
@孩子们=();
对于 $person ( @{ $rec->{members} } ) {
if ($person->{role} =~ /kid|son|daughter/) {
推@kids, $person;
}
}
# 记住:$rec 和 $TV{$family} 指向相同的数据!!
$rec->{kids} = [@kids];
}
# 你复制了数组,但数组本身包含指针
# 到未复制的对象。 这意味着如果你让 bart 得到
# 旧通过
$TV{辛普森一家}{孩子}[0]{年龄}++;
# 那么这也会改变
打印 $TV{simpsons}{members}[2]{age};
# 因为 $TV{simpsons}{kids}[0] 和 $TV{simpsons}{members}[2]
# 两者都指向同一个底层匿名哈希表
# 打印整个内容
foreach $family ( 键 %TV ) {
打印“$family”;
打印“在@{ $TV{$family}{nights} }\n 期间播放;
print "它的成员是:\n";
对于 $who ( @{ $TV{$family}{members} } ) {
打印 " $who->{name} ($who->{role}), 年龄 $who->{age}\n";
}
打印“事实证明 $TV{$family}{lead} 有”;
打印标量 (@{ $TV{$family}{kids} } ), " kids named ";
print join (", ", map { $_->{name} } @{ $TV{$family}{kids} } );
打印“\n”;
}
数据库 领带
您不能轻易地将多级数据结构(例如散列的散列)绑定到 dbm
文件。 第一个问题是除了 GDBM 和 Berkeley DB 之外的所有数据库都有大小限制,但是
除此之外,您还会遇到如何在磁盘上表示引用的问题。
一个尝试部分满足这一需求的实验模块是 MLDBM
模块。 检查离您最近的 CPAN 站点,如 perlmodlib 中所述,以获取 MLDBM 的源代码。
使用 onworks.net 服务在线使用 perldsc