我一直在努力使perl程序工作数周。其他人写了它,从那以后数据源已被更改。我花了几周时间逐行搜索并做了教程。我被卡住了。代码显示@{ $Routings{$Code} }
,其中包含值[ $ProcessID, $Setup, $Process ]
的列表,但在代码底部foreach ( @{ $Routings{$Code} } ) {my $ProcessCodeID = @$_[0];}
它似乎没有返回数据。如果有人甚至可以帮助我print $ProcessCodeID
,那么我可以跟踪数据,这将非常有帮助。
另外,如果你能解释一下@{$value{$key}}
所代表的内容,那也会有所帮助。
谢谢你。
%Routings = ();
my $dbh = DBI-> connect('dbi:ODBC:SQL')
or die "Couldn't open Databaxe: $DBI::errstr; stopped";
my $query= $dbh->prepare("SELECT Code, Setup, Process, ProcessID FROM ROUTING");
$query->execute() or die "Couldn't execute statement: $DBI::errstr; stopped";
while ( my ($Code, $setup, $process, $processid) = $query->fetchrow_array() ){
push ( @{ $Routings{$Code} }, [ $ProcessID, $Setup, $Process ] );
}
foreach ( @{ $Routings{$Code} } ) {
my $ProcessCodeID = @$_[0];
my $SetupMins = @$_[1];
my $ProcessMins = @$_[2];
}
答案 0 :(得分:6)
首先,您必须在程序开始时use strict
和use warnings
,并在首次使用它们时声明所有变量。这将导致Perl生成一些非常有用的消息,这些消息将揭示许多容易被忽视的简单错误。
例如,您要分配变量$setup
,$process
和$processid
,然后推送$Setup
,$Process
和{{1}到数组上。 Perl标识符区分大小写,因此这些是三个不同的变量,此时的值为$ProcessID
。 undef
会打印出一个编译错误,指出use strict
等未被声明。 (如果你有选择的话,最好使用小写加下划线来表示这些本地标识符。经验丰富的Perl程序员会感谢你。)
您应该试验Data::Dumper
module,它将显示复杂的嵌套Perl数据结构的内容和结构。在程序中$ProcessID
后,您可以编写
use Data::Dumper
将print Dumper \%Routings
的内容显示为匿名哈希。
哈希的每个元素%Routings
的值是与该代码值对应的所有ProcessID,Setup和Process集合的列表(对数组的引用)。 (我假设列$Routings{$Code}
是非唯一的,否则数据结构比它需要的更复杂。)因此,给定Code
的第一组三个值位于{{1}该集合的$Code
为$Routings{$Code}[0]
。
没有代码可以为ProcessID
循环为$Routings{$Code}[0][0]
分配值,并且可能您希望循环遍历$Code
哈希的所有键。
每次将foreach
循环%Routings
设置为对当前foreach
的每个三元组值的引用。这意味着$_
是一个三元素数组,但它应该使用$Code
等而不是@$_
进行索引,这是一个单元素数组切片和编码实践不佳。在这里使用默认的$_->[0]
使代码变得更加模糊,我在下面通过使用命名变量对其进行了澄清。
下面的代码修复了我能看到的问题。如果您需要任何进一步的帮助,请回来。
@$_[0]
请注意,$_
循环中的分配可以通过一次执行它们来更清晰,更简洁。正如我所解释的那样,use strict;
use warnings;
use DBI;
my %Routings;
my $dbh = DBI-> connect('dbi:ODBC:SQL')
or die "Couldn't open Databaxe: $DBI::errstr; stopped";
my $query= $dbh->prepare("SELECT Code, Setup, Process, ProcessID FROM ROUTING");
$query->execute or die "Couldn't execute statement: $DBI::errstr; stopped";
while ( my ($Code, $Setup, $Process, $ProcessID) = $query->fetchrow_array ){
push @{ $Routings{$Code} }, [ $ProcessID, $Setup, $Process ];
}
for my $Code (keys %Routings) {
foreach my $triplet ( @{ $Routings{$Code} } ) {
my $ProcessCodeID = $triplet->[0];
my $SetupMins = $triplet->[1];
my $ProcessMins = $triplet->[2];
print "$Code => ($ProcessCodeID, $SetupMins, $ProcessMins)\n";
}
}
是一个三元素数组,因此等效赋值可以简单地编码为
foreach
(请谨慎对待此代码,因为我无法在没有重要工作设置测试数据库的情况下彻底测试它,尽管它在一个简单的数据集上可以正常工作。)
答案 1 :(得分:5)
代码按“代码”对“路由记录”进行分组。
%Routings
是一个哈希值。它由“代码”键入。每个值都是对数组的引用。这些数组由push ( @{ $Routings{$Code} },
自动生成,这是push ( @{ $Routings{$Code} //= [] },
的缩写。
每个阵列都包含许多“记录”。每个“记录”是对三个元素(“进程ID”,“设置”和“进程”)的数组的引用。它们由[ $ProcessID, $Setup, $Process ]
创建。
转储看起来像:
{
$code0 => [
[ $ProcessID0, $setup0, $Process0 ],
[ $ProcessID2, $setup2, $Process2 ],
...
],
$code1 => [
[ $ProcessID1, $setup1, $Process1 ],
[ $ProcessID5, $setup5, $Process5 ],
...
],
$code2 => [
[ $ProcessID3, $setup3, $Process3 ],
[ $ProcessID4, $setup4, $Process4 ],
...
],
...
}
如果$Code
具有有意义的值 - 您没有显示它获得值 - $Routings{$code}
将评估其中一个数组引用。从上面的例子中,
[
[ $ProcessID0, $setup0, $Process0 ],
[ $ProcessID2, $setup2, $Process2 ],
...
],
@{ ... }
向Perl表明您希望 derefence 该引用。换句话说,它告诉Perl你对数组本身感兴趣。
当您将数组传递给foreach时,它会遍历其元素。因此,第一次循环时,$_
将保留以下数组引用:
[ $ProcessID0, $setup0, $Process0 ],
第二次,
[ $ProcessID2, $setup2, $Process2 ],
等
@$_[0]
(@{ $_ }[0]
的缩写,它是一个使用不正确的${ $_ }[0]
,更可读,因为$_->[0]
)获取引用数组的第一个元素({{ 1}})。同样,$ProcessID0
和@$_[1]
获得@$_[2]
和$setup0
。
当然,你继续对数据一无所知。你可能打算做
$Process0
清理:
foreach ( @{ $Routings{$Code} } ) {
my $ProcessCodeID = ${$_}[0];
my $SetupMins = ${$_}[1];
my $ProcessMins = ${$_}[2];
print("$ProcessCodeID ,$SetupMins, $processMins\n");
}
清理了一些:
foreach ( @{ $Routings{$Code} } ) {
my $ProcessCodeID = $_->[0];
my $SetupMins = $_->[1];
my $ProcessMins = $_->[2];
print("$ProcessCodeID, $SetupMins, $processMins\n");
}
从技术上讲,你甚至可以做到
for ( @{ $Routings{$Code} } ) {
my ($ProcessCodeID, $SetupMins, $ProcessMins) = @$_;
print("$ProcessCodeID, $SetupMins, $processMins\n");
}
或
for ( @{ $Routings{$Code} } ) {
print(join(', ', @$_), "\n");
}
答案 2 :(得分:4)
您对Perl引用了解多少?您可能希望查看tutorials上的一些Perl references。
Perl的所有三个基本数据结构(标量,数组和散列)都旨在保存单个数据值。例如,我有一个Employees数组:
$employee_list[0] = "Bob";
$employee_list[1] = "Carol";
$employee_list[2] = "Ted";
$employee_list[3] = "Alice";
是的,我的数组中有四个数据,但在简单的Perl中,每个项目只包含一个值 - 一个名字。如果我还想要员工的姓,工资或职称,我该怎么办?在基本的Perl数据结构中没有简单的方法。
引用是一种允许您在Perl变量中存储多个数据的方法。让我们来看看鲍勃的完整员工记录:
$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哈希。现在,问题是如何访问这些信息?
我可以通过解除引用来访问我的参考资料。您可以通过在引用前面添加正确的sigil来实现此目的:
$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]
解除引用哈希 和 获取该哈希值,并在同一步骤中检索特定键的值。请注意,我使用的是$
而不是%
。
正如您所看到的,它可以快速复杂化。但是,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"," ADDRESS",以及其他填充数据的键。 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
的子程序,允许我设置员工的名字。此子例程称为方法,有时称为成员函数。
因此,回到程序,我们创建了一个表示数据库连接的对象。如果需要,可以使用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语句。准备好之后,我执行它。执行真的是命中数据库。请注意,我的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
和{{ 1}}。
现在,我们正在进入foreach循环和另一个错误。 $ProcessID
的价值是什么?它没有任何价值,因为变量$Code
仅存在于上面的$Code
循环中。当您使用while
声明变量时,一旦离开代码块,此变量的值就会丢失。
如果您的计划顶部有use strict;
和use warnings;
,则可能会发现此错误,而上述错误可能已被抓住。
让我们来看看这个循环:
my
此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
不仅是数组引用,而且该数组中的每个条目都是对另一个数组的引用。这是一个array of arrays。
因此,我$Routings{$Code}
中的每个条目都是对另一个我再次引用的数组的引用。现在我只是获取每个数组元素的值并将其放入常规的Perl标量变量中。
很抱歉很长的解释,但是您有一些代码涉及引用,类,方法,构造函数,对象以及一大堆相当高级的Perl主题。以及我指出的一些错误。可能使用几个标准Perl编译指示捕获的错误:@routing_code_ref_array
和use strict;
。
如果有任何东西可以拿走,那就是:
use warnings;
或@$foo{$bar}[4]
或(更正确)@{ $foo{bar} }[4]
或(更明确地)${ $foo{bar} }[4]
等内容是对更复杂数据结构的引用。基本的Perl数据结构一次只能容纳一个项目。通过使用对其他数据结构的引用,您可以拥有数组的数组或哈希或散列数组的散列或散列数组,甚至是哈希数组散列的散列数组。不要惊慌,并试图从内到外解析这些事情。如果您能够在多行中处理特别复杂的数据结构,有时会更容易。$foo{bar}->[4]
就是您的朋友。它将快速揭示这些过于复杂的结构的结构,并可以帮助您调试程序的问题。Data::Dumper
和strict
。这些将会引起很多编程的蠢事。正如我所说,我发现两个与局部变量的范围相关,而在变量名称的情况下则是错误的。标准化变量名称也是一个很棒的主意。这两种方法是camelCasing,只使用下划线和小写字母。这样,您就知道它总是warnings
而永远不会$foo_bar
或$Foo_Bar
或$FooBar
。旧标准是骆驼外壳,第一个字母是小写字母。新标准仅使用小写字母和下划线。答案 3 :(得分:0)
实际上并不是尝试到return
数据 - 它只是用数据创建变量,然后立即对数据一无所知。试试这个:
foreach ( @{ $Routings{$Code} } ) {
my $ProcessCodeID = @$_[0];
my $SetupMins = @$_[1];
my $ProcessMins = @$_[2];
print "$Code: $ProcessCodeID, $SetupMins, $ProcessMins\n";
}
除非你在循环中使用变量,否则没有多大意义。
复杂的ish @{ $foo{$bar} }
结构告诉Perl将$foo{$bar}
视为数组。 $foo{$bar}
是哈希查找。 (参见顶部的%Routings = ();
?声明并初始化哈希值。)
Perl绝对是整洁的,但这样的事情足以让我决定用Ruby等新语言编写新代码。这个代码在Ruby中可能也不是那么好,并且精通任何一个人的人都不会真正关心,但你可能希望借此机会重新编写工具,因为你需要它们。