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

use*_*197 4 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];
}
Run Code Online (Sandbox Code Playgroud)

Bor*_*din 6

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

作为一个例子,要分配的变量$setup,$process$processid但随后推动$Setup,$Process$ProcessID到阵列.Perl标识符区分大小写,因此这些是三个不同的变量,此时将具有值undef.use strict会打印出一个编译错误,说$ProcessID没有声明等等.(如果你有一个选择,最好使用小写加下划线来表示这些本地标识符.经验丰富的Perl程序员会感谢你.)

您应该试验一下这个Data::Dumper模块,它将显示一个复杂的嵌套Perl数据结构的内容和结构.一旦你use Data::Dumper进入你的程序,你就可以写

print Dumper \%Routings
Run Code Online (Sandbox Code Playgroud)

这将显示%Routings匿名哈希的内容.

$Routings{$Code}散列的每个元素的值是对应于Code的值的所有ProcessID,Setup和Process集合的列表(对数组的引用).(我假设该列Code是非唯一的,否则数据结构比它需要的更复杂.)因此,给定的第一组三个值$Code是at $Routings{$Code}[0],ProcessID而该组是$Routings{$Code}[0][0].

没有代码$Codeforeach循环分配值,并且可能您希望遍历%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";
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,foreach通过一次执行所有循环,可以使循环内的分配更清晰,更简洁.正如我所解释的那样,@$triplet是一个三元素数组,所以等效赋值可以简单编码

my ($ProcessCodeID, $SetupMins, $ProcessMins) = @$triplet;
Run Code Online (Sandbox Code Playgroud)

(请谨慎对待此代码,因为我无法在没有重要工作设置测试数据库的情况下彻底测试它,尽管它在一个简单的数据集上可以正常工作.)

  • 非常感谢.我错过了大小写的区别.这现在有效.我也很感激解释并帮助清理我的代码.非常感谢你. (2认同)

ike*_*ami 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 ],
      ...
   ],
   ...
}
Run Code Online (Sandbox Code Playgroud)

如果$Code有一个有意义的值 - 你没有显示它获得一个值 - $Routings{$code}将评估其中一个数组引用.从上面的例子来看,

[
   [ $ProcessID0, $setup0, $Process0 ],
   [ $ProcessID2, $setup2, $Process2 ],
   ...
],
Run Code Online (Sandbox Code Playgroud)

@{ ... }向Perl表明您想要拒绝该引用.换句话说,它告诉Perl你对数组本身感兴趣.

将数组传递给foreach时,它会遍历其元素.所以第一次通过循环,$_将保持以下数组引用:

[ $ProcessID0, $setup0, $Process0 ],
Run Code Online (Sandbox Code Playgroud)

第二次,

[ $ProcessID2, $setup2, $Process2 ],
Run Code Online (Sandbox Code Playgroud)

等等

@$_[0](缩写@{ $_ }[0],使用不当${ $_ }[0],更易读$_->[0])获取引用数组的第一个元素($ProcessID0).同样,@$_[1]@$_[2]得到$setup0$Process0.


当然,那么你继续对数据一无所知.你可能想做

foreach ( @{ $Routings{$Code} } ) {
    my $ProcessCodeID = ${$_}[0];
    my $SetupMins     = ${$_}[1];
    my $ProcessMins   = ${$_}[2];
    print("$ProcessCodeID ,$SetupMins, $processMins\n");
}
Run Code Online (Sandbox Code Playgroud)

清理:

foreach ( @{ $Routings{$Code} } ) {
    my $ProcessCodeID = $_->[0];
    my $SetupMins     = $_->[1];
    my $ProcessMins   = $_->[2];
    print("$ProcessCodeID, $SetupMins, $processMins\n");
}
Run Code Online (Sandbox Code Playgroud)

清理了一些:

for ( @{ $Routings{$Code} } ) {
    my ($ProcessCodeID, $SetupMins, $ProcessMins) = @$_;
    print("$ProcessCodeID, $SetupMins, $processMins\n");
}
Run Code Online (Sandbox Code Playgroud)

从技术上讲,你甚至可以做到

for ( @{ $Routings{$Code} } ) {
    print(join(', ', @$_), "\n");
}
Run Code Online (Sandbox Code Playgroud)

要么

print(join(', ', @$_), "\n")
   for @{ $Routings{$Code} };
Run Code Online (Sandbox Code Playgroud)


Dav*_* W. 5

您对 Perl 参考资料了解多少?您可能想查看一些有关Perl 参考资料的教程

快速参考教程

Perl 的所有三种基本数据结构(标量、数组和散列)都被设计为保存单个数据值。例如,我有一组员工:

$employee_list[0] = "Bob";
$employee_list[1] = "Carol";
$employee_list[2] = "Ted";
$employee_list[3] = "Alice";
Run Code Online (Sandbox Code Playgroud)

是的,我的数组中有四块数据,但在简单的 Perl 中,每一项只包含一个值——名字。如果我还想要员工的姓氏、工资或职位,我该怎么办?在基本的 Perl 数据结构中没有简单的方法可以做到这一点。

引用是一种允许您在 Perl 变量中存储多条数据的方法。让我们看一下 Bob 的完整员工记录:

$employee{FIRST}  = "Bob";
$employee{LAST}   = "Jones";
$employee{PAY}    = "1400";
$employee{PHONE}  = "1234";
Run Code Online (Sandbox Code Playgroud)

现在,我怎样才能将所有这些信息压缩到 中$employee_list[0]

Perl 允许您引用此散列%employee主要是内存中存储该散列的位置。您可以通过在其前面放置反斜杠来实现此目的:

$employee_list[0] = \%employee;
Run Code Online (Sandbox Code Playgroud)

现在,在那个$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";
Run Code Online (Sandbox Code Playgroud)

我首先获得引用,然后我可以将其取消引用到新的%employee_hash. 一旦我这样做了,我就可以使用哈希中的信息。这是很多工作。看着$employee_reference。我所做的就是获取引用,这样我就可以取消引用它。为什么不删除该步骤并直接从 中获取我的取消引用$employee_list[0]

%employee_hash      = %{ $employee_list[0] };
print "Employee name is $employee_hash{FIRST} $employee_hash{LAST}\n";
Run Code Online (Sandbox Code Playgroud)

请注意,我在引用周围使用了花括号。大括号有点像方程两边的括号。他们让 Perl 知道首先要做什么。

不过,再说一遍,我并没有真正对%employee_hash. 这只是一个我可以扔散列的地方,这样我就可以打印它。为什么不取消引用散列,并一步获取特定键的值?甚至更好。只需一步:

print "Employee name is "
   . ${ $employee_list[0] }{FIRST} . " "
   . ${ $employee_list[0] }{LAST} . "\n";
Run Code Online (Sandbox Code Playgroud)

我取消$employee_list[0]引用哈希值获取该哈希值,并在同一步骤中检索特定键的值。请注意,我使用 a$而不是%

正如您所看到的,它很快就会变得复杂。然而,Perl 为您提供了一种很好的方式来表示这种过于复杂的结构:

print "Employee name is " 
  . $employee_list[0]->{FIRST} . " " 
  . $employee_list[0]->{LAST} . "\n";
Run Code Online (Sandbox Code Playgroud)

操作员为您->取消引用

%employee_hash我构建一个名为的哈希值只是为了引用它,这也有点愚蠢。Perl 允许您引用匿名散列和数组。

$employee_list[0] = { LAST => "Jones", FIRST => "Bob",
    SALARY => 1400, PHONE => "1234" }
Run Code Online (Sandbox Code Playgroud)

大括号用于匿名哈希。方括号用于匿名数组。它们是匿名的,因为它们不引用变量,而只是对哈希或数组的引用。

数据::转储器

正如您可以想象的那样,这些数据结构可能会变得非常复杂。例如,我跟踪员工的地址,但地址由街道、城市、州和邮政编码组成。有时,街上的队伍不止一条。而且,如果有多个地址怎么办?哈希或数组引用没有理由不能包含对另一个哈希或数组的引用:

$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";
Run Code Online (Sandbox Code Playgroud)

正如您所看到的 $employee_list[0] 指向对员工哈希的引用。该散列有“NAME”、“ADDRESS”和其他填充数据的键。该NAME字段是对另一个具有两个键的哈希的引用:FIRSTLAST。该ADDRESS字段实际上是对地址数组的引用。每个数组条目都是对哈希的引用。想象一下尝试调试这个数据结构!

Data::Dumper是一个模块,它将解析最复杂的数据结构并为您打印出来:

use Data::Dumper;

[...]

print "Employee Dump: " . Dumper \@employee . "\n";
Run Code Online (Sandbox Code Playgroud)

这将打印出员工数组中所有员工的整个结构。

如果您不知道@{$value{$key}}是什么,您可以轻松地对其运行转储:

print Dumper $value{$key} . "\n";
Run Code Online (Sandbox Code Playgroud)

解码你的程序

让我们一一过一遍:

%Routings = ();
my $dbh = DBI->connect('dbi:ODBC:SQL')
    or die "Couldn't open Databaxe: $DBI::errstr;  stopped";
Run Code Online (Sandbox Code Playgroud)

您初始化了一个名为的哈希%Routings并创建了一个代表与数据库的连接的 DBI 对象。connect是在 Perl 中定义为 DBI 类一部分的子例程。所有类都由一堆 Perl 子例程组成,这些子例程对由该类创建的对象进行操作。这些子例程分为构造函数方法。构造函数创建对表示对象的复杂数据结构的引用。方法是可以对该对象进行操作的子例程。想象一下我们的员工记录:

$employee = Person::Employee->new;
$employee->first_name( "Bob" );
Run Code Online (Sandbox Code Playgroud)

第一行$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";
Run Code Online (Sandbox Code Playgroud)

我现在准备我想要执行的 SQL 语句。准备好后,我就执行它。执行实际上是对数据库的影响。请注意, myprepare是数据库句柄的方法$dbi,但它也是构造函数,因为它创建了$query对象。

我使用该$query对象来实际执行我的查询。再次强调,不要害怕使用Data::Dumper它来打印它。

while ( my ($Code, $setup, $process, $processid) = $query->fetchrow_array() ){
    push ( @{ $Routings{$Code} }, [ $ProcessID, $Setup, $Process ] );
}
Run Code Online (Sandbox Code Playgroud)

让我们稍微简化一下:

while ( my @fetched_row = $query->fetchrow_array() ){
    my ($Code, $setup, $process, $processid) = @fetched_row;
    push ( @{ $Routings{$Code} }, [ $ProcessID, $Setup, $Process ] );
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

[ $ProcessID, $Setup, $Process ]只是创建对匿名数组的引用。这节省了我们创建 的引用@temp_array并将其推@temp_array送到我的@temp_routing_array.

而且,当我们这样做时,您的代码中有一个错误。我正在获取$setup$process$processid,但我正在存储(注意变量名称的大小写)$Setup$Process$ProcessID

现在,我们开始讨论 foreach 循环和另一个 bug。有什么价值$Code?它没有任何值,因为该变量$Code只存在于while上面的循环中。当您使用 声明变量时my,一旦离开代码块,该变量的值就会丢失。

如果您在程序的顶部有use strict;和,则可以捕获此错误以及上面的错误。use warnings;

让我们看看这个循环:

foreach ( @{ $Routings{$Code} } ) {
    my $ProcessCodeID = @$_[0];
    my $SetupMins = @$_[1];
    my $ProcessMins = @$_[2];
}
Run Code Online (Sandbox Code Playgroud)

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];
}
Run Code Online (Sandbox Code Playgroud)

请记住,这$Routings{$Code}是对数组的引用。在我的第一行中,我取消引用它。在原始代码中,取消引用发生在foreach循环中。不仅是$Routings{$Code}一个数组引用,而且该数组中的每个条目都是对另一个数组的引用。它是一个数组的数组

因此, my 中的每个条目@routing_code_ref_array都是对另一个数组的引用,我再次取消引用该数组。现在我只是获取每个数组元素的值并将其放入常规 Perl 标量变量中。

已经足够!

抱歉解释太长,但是您有一些代码涉及引用、类、方法、构造函数、对象和一大堆相当高级的 Perl 主题。以及我指出的一些错误。可以使用几个标准 Perl 编译指示捕获的错误:use strict;use warnings;

如果说有什么东西是你可以带走的,那就是:

  • @$foo{$bar}[4]or @{ $foo{bar} }[4]or (更正确地)${ $foo{bar} }[4]or (更清楚地)这样的东西$foo{bar}->[4]是对更复杂的数据结构的引用。基本的 Perl 数据结构一次只能保存一个项目。通过使用对其他数据结构的引用,您可以拥有数组的数组、散列数组、散列的散列、数组的散列,甚至散列数组的散列的散列数组。不要惊慌,尝试从内到外解析这些事情。有时,如果您可以在多行中处理特别复杂的数据结构,则会更容易。
  • 如果您要遇到复杂的结构,Data::Dumper那就是您的朋友。它将快速揭示这些过于复杂的结构,并可以帮助您调试程序问题。
  • 在你的程序中使用strictand 。warnings这些会发现很多编程错误。正如我所说,我发现两个与局部变量的范围以及变量名称的错误输入有关。标准化变量名称也是一个伟大的想法。这两种方法是驼峰命名法和仅使用下划线和小写字母。这样,您就知道它总是$foo_bar或永远不会$Foo_Bar$FooBar$fooBar。旧标准是驼峰式大小写,第一个字母是小写字母。新标准仅使用小写字母和下划线。