你对 Perl 的引用了解多少?您可能想查看一些关于Perl 参考的教程。
快速参考教程
Perl 的所有三种基本数据结构(标量、数组和散列)都旨在保存单个数据值。例如,我有一个员工数组:
$employee_list[0] = "Bob";
$employee_list[1] = "Carol";
$employee_list[2] = "Ted";
$employee_list[3] = "Alice";
是的,我的数组中有四段数据,但在简单的 Perl 中,每一项都只包含一个值——名字。如果我还想要员工的姓氏、薪水或职位,我该怎么办?在基本的 Perl 数据结构中没有简单的方法可以做到这一点。
引用是一种允许您在 Perl 变量中存储多条数据的方法。让我们看一下 Bob 的完整员工记录:
$employee{FIRST} = "Bob";
$employee{LAST} = "Jones";
$employee{PAY} = "1400";
$employee{PHONE} = "1234";
现在,我怎样才能将所有这些信息压缩到$employee_list[0]
?
Perl 允许你引用这个散列%employee
(主要是内存中存储散列的位置。你可以通过在它前面放一个反斜杠来做到这一点:
$employee_list[0] = \%employee;
现在,在那个$employee_list[0]
槽中,我引用了一个包含 Bob 的所有员工信息的 Perl 哈希。现在,问题是如何访问这些信息?
我可以通过取消引用来访问我的参考资料中的信息。您可以通过将正确的印记放在参考前面来做到这一点:
$employee_reference = $employee_list[0];
%employee_hash = %$employee_reference;
print "Employee name is $employee_hash{FIRST} $employee_hash{LAST}\n";
我首先得到引用,然后我可以将它取消引用到一个新的%employee_hash
. 一旦我这样做了,我就可以使用散列中的信息。这是很多工作。看$employee_reference
。我所做的只是获取参考,所以我可以取消参考。为什么不删掉那一步,直接从$employee_list[0]
?
%employee_hash = %{ $employee_list[0] };
print "Employee name is $employee_hash{FIRST} $employee_hash{LAST}\n";
注意我在我的参考文献周围使用了花括号。花括号有点像方程周围的括号。他们让 Perl 知道首先要做什么。
不过,同样,我并没有真正对%employee_hash
. 这只是一个我可以扔散列的地方,所以我可以打印它。为什么不取消引用哈希,并一步获取特定键的值?甚至更好。一步:
print "Employee name is "
. ${ $employee_list[0] }{FIRST} . " "
. ${ $employee_list[0] }{LAST} . "\n";
我正在取消引用$employee_list[0]
哈希并获取该哈希,并在同一步骤中检索特定键的值。请注意,我使用 a$
而不是 a %
。
如您所见,它会很快变得复杂。然而,Perl 为您提供了一种很好的方式来表示这种过于复杂的结构:
print "Employee name is "
. $employee_list[0]->{FIRST} . " "
. $employee_list[0]->{LAST} . "\n";
->
操作员为您取消引用。
我建立一个哈希调用%employee_hash
只是为了引用它也有点愚蠢。Perl 允许您引用匿名哈希和数组。
$employee_list[0] = { LAST => "Jones", FIRST => "Bob",
SALARY => 1400, PHONE => "1234" }
花括号用于匿名哈希。方括号用于匿名数组。它们是匿名的,因为它们不引用变量,而只是对哈希或数组的引用。
数据::自卸车
可以想象,这些数据结构会变得相当复杂。例如,我跟踪员工的地址,但地址由街道、城市、州和邮政编码组成。有时,这条街的线路不止一条。而且,如果有多个地址怎么办?哈希或数组引用没有理由不能包含对另一个哈希或数组的引用:
$employee_list[0]->{NAME}->{FIRST} = "Bob";
$employee_list[0]->{NAME}->{LAST} = "Jones";
$employee_list[0]->{ADDRESS}->[0]->{TYPE} = "Business";
$employee_list[0]->{ADDRESS}->[0]->{STREET}->[0] = "123 Mockingbird Lane";
$employee_list[0]->{ADDRESS}->[0]->{STREET}->[1] = "Tower 2";
$employee_list[0]->{ADDRESS}->[0]->{CITY} = "Beantown";
$employee_list[0]->{ADDRESS}->[0]->{STATE} = "MA";
如您所见,$employee_list[0] 指向对员工哈希的引用。该哈希具有“名称”、“地址”和其他填充数据的键。该NAME
字段是对另一个具有两个键的散列的引用:FIRST
和LAST
。该ADDRESS
字段实际上是对地址数组的引用。这些数组条目中的每一个都是对哈希的引用。想象一下尝试调试这个数据结构!
Data::Dumper是一个模块,它将解析最复杂的数据结构并为您打印出来:
use Data::Dumper;
[...]
print "Employee Dump: " . Dumper \@employee . "\n";
这将打印出员工数组中所有员工的整个结构。
如果您不知道是什么@{$value{$key}}
,您可以轻松地在其上运行转储:
print Dumper $value{$key} . "\n";
解码你的程序
让我们逐行浏览:
%Routings = ();
my $dbh = DBI->connect('dbi:ODBC:SQL')
or die "Couldn't open Databaxe: $DBI::errstr; stopped";
您初始化了一个名为的散列%Routings
并创建了一个 DBI 对象,该对象表示与您的数据库的连接。connect
是在 Perl 中作为 DBI 类的一部分定义的子例程。所有的类都由一堆 Perl 子例程组成,这些子例程对由该类创建的对象进行操作。这些子例程分为构造函数和方法。构造函数创建对表示对象的复杂数据结构的引用。方法是可以对该对象进行操作的子程序。想象一下我们的员工记录:
$employee = Person::Employee->new;
$employee->first_name( "Bob" );
第一行从我的班级创建一个$employee
对象。Person::Employee
该$employee
对象实际上只是对包含我的员工信息的哈希的引用。所以,我的子程序new
是一个构造函数。
第二行使用一个名为的子例程first_name
,它允许我设置员工的名字。该子例程称为Method或有时称为Member Function。
所以,回到程序,我们创建了一个代表我们的数据库连接的对象。如果你愿意,你可以用它Data::Dumper
来打印出这个对象的结构,如果这有助于你更好地理解它。它只是对哈希的引用。
my $query= $dbh->prepare("SELECT Code, Setup, Process, ProcessID FROM ROUTING");
$query->execute() or die "Couldn't execute statement: $DBI::errstr; stopped";
我现在准备要执行的 SQL 语句。在我准备好之后,我执行它。执行实际上是对数据库的影响。请注意,my是数据库句柄prepare
的一个方法$dbi
,但它也是一个构造函数,因为它创建了$query
对象。
我使用该$query
对象来实际执行我的查询。同样,不要害怕使用Data::Dumper
打印出来。
while ( my ($Code, $setup, $process, $processid) = $query->fetchrow_array() ){
push ( @{ $Routings{$Code} }, [ $ProcessID, $Setup, $Process ] );
}
让我们稍微简化一下:
while ( my @fetched_row = $query->fetchrow_array() ){
my ($Code, $setup, $process, $processid) = @fetched_row;
push ( @{ $Routings{$Code} }, [ $ProcessID, $Setup, $Process ] );
}
这fetchrow_array
是一个从我的查询中获取一行作为列数组的子例程。这个子例程是我上面创建的对象的一个方法。$query
我所做的只是从我的数据库中获取每一行并将其放入四个 Perl 标量变量中。
最后一行有点棘手。还记得我%Routings
初始化的哈希吗?显然,此散列中的每个键都是对值数组的引用。哈希是$Code
我在上面提取的,它指向一个由 、 和 组成的三$ProcesssID
成员$Setup
数组$Process
。我们可以像这样重写第三行:
my @temp_array = ($ProcessID, $Setup, $Process);
my @temp_routing_array = @{ $Routings{Code} }; #Dereferencing the $Routing{$Code} array
push( @temp_routing_array, \@temp_array ); #Pushing a reference into my array
$Routing{$Code} = \@temp_routing_array; #Creating a reference again
这[ $ProcessID, $Setup, $Process ]
只是创建对匿名数组的引用。这为我们省去了创建@temp_array
然后将引用推@temp_array
送到我的@temp_routing_array
.
而且,当我们这样做时,您的代码中有一个错误。我正在获取$setup
、$process
和$processid
,但我正在存储(注意变量名的大小写)$Setup
、$Process
和$ProcessID
。
现在,我们进入 foreach 循环和另一个错误。的价值是$Code
多少?它没有任何价值,因为变量$Code
只存在于上面的while
循环中。当你用 声明一个变量时my
,一旦你离开一段代码,这个变量的值就会丢失。
use strict;
如果您有并且use warnings;
在程序的顶部,则可能会捕获此错误以及上述错误。
让我们看看这个循环:
foreach ( @{ $Routings{$Code} } ) {
my $ProcessCodeID = @$_[0];
my $SetupMins = @$_[1];
my $ProcessMins = @$_[2];
}
此foreach
循环使用过时的样式,您假设您正在循环访问$_
变量。它令人困惑,大多数人已经学会不使用它。让我们重写它:
my @routing_code_ref_array = @{ $Routings{$Code} };
foreach my $routing_array_ref (@routing_code_ref_array) {
my @routing_array = @{ $routing_array_ref };
my $ProcessCodeID = $routing_array[0];
my $SetupMins = $routing_array[1];
my $ProcessMins = $routing_array[2];
}
请记住,这$Routings{$Code}
是对数组的引用。在我的第一行中,我取消引用它。在原始代码中,取消引用发生在foreach
循环中。不仅是$Routings{$Code}
一个数组引用,而且该数组中的每个条目都是对另一个数组的引用。它是一个数组数组。
因此,我@routing_code_ref_array
的每个条目都是对另一个数组的引用,我再次取消引用。现在我只是将每个数组元素的值放入常规的 Perl 标量变量中。
已经足够!
抱歉,解释太长了,但是您有一些涉及引用、类、方法、构造函数、对象和一大堆相当高级的 Perl 主题的代码。以及我指出的一些错误。可以用几个标准 Perl pragma 捕获的错误:use strict;
和use warnings;
.
如果有什么可以带走的,那就是:
@$foo{$bar}[4]
或@{ $foo{bar} }[4]
或(更准确地说)${ $foo{bar} }[4]
或(更清楚地)之类的东西$foo{bar}->[4]
是对更复杂数据结构的引用。基本的 Perl 数据结构一次只能保存一个项目。通过使用对其他数据结构的引用,您可以拥有数组的数组或哈希数组或哈希的哈希或数组的哈希,甚至哈希数组的哈希数组。不要惊慌,并尝试从内到外解析这些事情。有时,如果您可以在多行中处理特别复杂的数据结构,会更容易。
- 如果你要遇到复杂的结构,
Data::Dumper
是你的朋友。它将快速揭示这些过于复杂的结构的结构,并可以帮助您调试程序的问题。
- 在您的程序中使用
strict
和。warnings
这些会发现很多编程问题。正如我所说,我发现两个与局部变量的范围有关,并且在变量名的情况下输入错误。标准化变量名也是一个伟大的想法。这两种方法是 camelCasing 并且只使用下划线和小写字母。这样,您就知道它总是$foo_bar
并且从不$Foo_Bar
or $FooBar
or $fooBar
。旧标准是驼峰式大小写,第一个字母是小写字母。新标准仅使用小写字母和下划线。