perl中的(@ {$ {{value}})是什么?

时间:2012-04-18 03:02:29

标签: perl

我一直在努力使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];
}

4 个答案:

答案 0 :(得分:6)

首先,您必须在程序开始时use strictuse warnings,并在首次使用它们时声明所有变量。这将导致Perl生成一些非常有用的消息,这些消息将揭示许多容易被忽视的简单错误。

例如,您要分配变量$setup$process$processid,然后推送$Setup$Process和{{1}到数组上。 Perl标识符区分大小写,因此这些是三个不同的变量,此时的值为$ProcessIDundef会打印出一个编译错误,指出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字段是对具有两个密钥的另一个哈希的引用:FIRSTLASTADDRESS字段实际上是对地址数组的引用。每个数组条目都是对哈希的引用。想象一下尝试调试这个数据结构!

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_arrayuse strict;

如果有任何东西可以拿走,那就是:

  • use warnings;@$foo{$bar}[4]或(更正确)@{ $foo{bar} }[4]或(更明确地)${ $foo{bar} }[4]等内容是对更复杂数据结构的引用。基本的Perl数据结构一次只能容纳一个项目。通过使用对其他数据结构的引用,您可以拥有数组的数组或哈希或散列数组的散列或散列数组,甚至是哈希数组散列的散列数组。不要惊慌,并试图从内到外解析这些事情。如果您能够在多行中处理特别复杂的数据结构,有时会更容易。
  • 如果您要遇到复杂的结构,$foo{bar}->[4]就是您的朋友。它将快速揭示这些过于复杂的结构的结构,并可以帮助您调试程序的问题。
  • 在您的计划中使用Data::Dumperstrict。这些将会引起很多编程的蠢事。正如我所说,我发现两个与局部变量的范围相关,而在变量名称的情况下则是错误的。标准化变量名称也是一个很棒的主意。这两种方法是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中可能也不是那么好,并且精通任何一个人的人都不会真正关心,但你可能希望借此机会重新编写工具,因为你需要它们。