4

I have been working for weeks on trying to make a perl program work. Someone else wrote it and since then the data source has been changed. I have spent weeks searching line by line and doing tutorials. I am stuck. The code says @{ $Routings{$Code} } which has a list of values [ $ProcessID, $Setup, $Process ] but at the bottom of the code when foreach ( @{ $Routings{$Code} } ) {my $ProcessCodeID = @$_[0];} it does not seem to be returning the data. If anyone could even help me print $ProcessCodeID so I can track the data it would be extremely helpful.

Also if you could explain what @{$value{$key}} represents that would really help too.

Thanks heaps.

%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

4 に答える 4

6

First of all it is important that you use strict and use warnings at the start of your program, and declare all your variables at the point they are first used. This will cause Perl to generate some very useful messages that will reveal many simple errors that are easily overlooked.

As an example, you are assigning the variables $setup, $process, and $processid but then pushing $Setup, $Process, and $ProcessID onto an array. Perl identifiers are case-sensitive so these are three diffferent variables and will have a value of undef at this point. use strict would have printed a compilation error saying that $ProcessID etc. hadn't been declared. (If you have a choice, it is better to use lower-case plus underscore for local identifiers like these. Seasoned Perl programmers will thank you.)

You should experiment with the Data::Dumper module which will show the contents and structure of a complex nested Perl data structure like this. Once you have use Data::Dumper in your program you can write

print Dumper \%Routings

which will show the contents of %Routings as an anonymous hash.

The value of each element $Routings{$Code} of the hash is a list (a reference to an array) of all the sets of ProcessID, Setup, and Process that correspond to that value of Code. (I presume the column Code is non-unique, otherwise the data structure is more complex than it needs to be.) So the first set of three values for a given $Code is at $Routings{$Code}[0] and the ProcessID for that set is $Routings{$Code}[0][0].

There is no code to assign a value to $Code for the foreach loop, and presumably you would want to loop over all the keys of the %Routings hash.

Each time round the foreach loop $_ is set to a reference to each triplet of values for the current $Code. That means @$_ is a three-element array, but it should be indexed using $_->[0] etc. instead of @$_[0] which is a one-element array slice and poor coding practice. The code is made more obscure by using the default $_ here and I have clarified it below by using a named variable.

The code below fixes the problems I can see. Please come back if you need any further help.

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";
  }
}

Note that the assignments within the foreach loop can be made clearer and more concise by performing them all at once. As I have explained, @$triplet is a three-element array, so the equivalent assignment could be coded as simply

my ($ProcessCodeID, $SetupMins, $ProcessMins) = @$triplet;

(Please treat this code warily, as I cannot test it it thoroughly without significant work setting up a test database, although it does work correctly on a simple data set.)

于 2012-04-18T04:57:03.483 に答える
5

Perlリファレンスについてどのくらい知っていますか?Perlリファレンスに関するいくつかのチュートリアルを見たいと思うかもしれません。

クイックリファレンスチュートリアル

Perlの3つの基本的なデータ構造(スカラー、配列、ハッシュ)はすべて、単一のデータ値を保持するように設計されています。たとえば、私には一連の従業員がいます。

$employee_list[0] = "Bob";
$employee_list[1] = "Carol";
$employee_list[2] = "Ted";
$employee_list[3] = "Alice";

はい、私の配列には4つのデータがありますが、単純なPerlでは、各項目には1つの値(名)しか含まれていません。従業員の名前、給与、役職も必要な場合はどうすればよいですか?基本的なPerlデータ構造でそれを行う簡単な方法はありません。

参照は、Perl変数に複数のデータを格納できるようにする方法です。ボブの完全な従業員記録を見てみましょう。

$employee{FIRST}  = "Bob";
$employee{LAST}   = "Jones";
$employee{PAY}    = "1400";
$employee{PHONE}  = "1234";

では、どうすればそのすべての情報を絞り込め$employee_list[0]ますか?

Perlでは、このハッシュ(主にそのハッシュが格納されているメモリ内の場所)への参照を取得できます。これを行うには、その前に円記号を付けます。%employee

$employee_list[0] = \%employee;

さて、その1つの$employee_list[0]スロットに、ボブのすべての従業員情報を含む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。ハッシュを投げることができる場所なので、印刷することができます。ハッシュを逆参照して、特定のキーの値を1つのステップで取得してみませんか?さらに良い。単一のステップで:

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の2つのキーを持つ別のハッシュへの参照です。このフィールドは、実際にはアドレスの配列への参照です。そして、これらの配列エントリのそれぞれは、ハッシュへの参照です。このデータ構造をデバッグしようとしているところを想像してみてください。FIRSTLASTADDRESS

Data :: Dumperは、最も複雑なデータ構造を解析して出力するモジュールです。

use Data::Dumper;

[...]

print "Employee Dump: " . Dumper \@employee . "\n";

これにより、employee配列内のすべての従業員の構造全体が出力されます。

何であるかわからない場合@{$value{$key}}は、簡単にダンプを実行できます。

print Dumper $value{$key} . "\n";

プログラムのデコード

行を1つずつ見ていきましょう。

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

と呼ばれるハッシュを初期化%Routingsし、データベースへの接続を表すDBIオブジェクトを作成しました。connectDBIクラスの一部としてPerlで定義されているサブルーチンです。すべてのクラスは、そのクラスによって作成されたオブジェクトを操作する一連のPerlサブルーチンで構成されています。これらのサブルーチンは、コンストラクターメソッドに分けられます。コンストラクターは、オブジェクトを表す複雑なデータ構造への参照を作成します。メソッドは、そのオブジェクトを操作できるサブルーチンです。従業員の記録を想像してみてください。

$employee = Person::Employee->new;
$employee->first_name( "Bob" );

最初の行は、クラス$employeeからオブジェクトを作成します。Person::Employeeその$employeeオブジェクトは、実際には私の従業員情報を含むハッシュへの単なる参照です。したがって、私のサブルーチンnewコンストラクターです。

first_name2行目は、従業員の名を設定できるというサブルーチンを使用しています。このサブルーチンは、メソッドまたはメンバー関数と呼ばれることもあります。

そこで、プログラムに戻って、データベース接続を表すオブジェクトを作成しました。必要に応じて、このオブジェクトの構造を少しよく理解するのに役立つ場合は、このオブジェクト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ステートメントを準備します。準備してから実行します。実行は実際にデータベースに影響を与えるものです。preparemyはデータベースハンドルのメソッドですが、オブジェクト$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は、クエリから1つの行を列の配列としてフェッチするサブルーチンです。このサブルーチンは、上記で作成したオブジェクトのメソッドです。$query私がしているのは、データベースから各行をフェッチし、それを4つのPerlスカラー変数に入れることだけです。

最後の行は少し注意が必要です。%Routings初期化したハッシュを覚えていますか?どうやら、このハッシュの各キーは値の配列への参照です。ハッシュは、上記でフェッチしたキーが付けられており、これは、、、および$Codeで構成される3つのメンバーの配列を指します。3行目を次のように書き直すことができます。$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プラグマでキャッチされた可能性のあるエラー:use strict;およびuse warnings;

あなたが奪うことができる何かがあるならば、それは次のとおりです:

  • @$foo{$bar}[4]or@{ $foo{bar} }[4]または(より正確に)${ $foo{bar} }[4]または(より明確に)のようなもの$foo{bar}->[4]は、より複雑なデータ構造への参照です。基本的なPerlデータ構造は、一度に1つのアイテムしか保持できません。他のデータ構造への参照を使用することにより、配列の配列またはハッシュの配列またはハッシュのハッシュまたは配列のハッシュ、さらにはハッシュの配列のハッシュの配列を使用できます。慌てずに、これらのことを裏返しに解析してみてください。特に複雑なデータ構造を複数行で処理できると、より簡単な場合があります。
  • あなたが複雑な構造に遭遇するつもりなら、Data::Dumperあなたの友達です。これらの非常に複雑な構造の構造をすばやく明らかにし、プログラムの問題をデバッグするのに役立ちます。
  • strictとをプログラムで使用warningsします。これらは多くのプログラミングの失敗を拾います。私が言ったように、私はローカル変数のスコープと変数名の場合のタイプミスの両方に関連する2つを見つけました。変数名を標準化することも素晴らしいアイデアです。2つの方法は、キャメルケースとアンダースコアと小文字のみを使用する方法です。このように、あなたはそれが常に$foo_barそして決して$Foo_Barまたは$FooBarまたはではないことを知っています$fooBar。古い標準は、最初の文字が小文字であるキャメルケースでした。新しい標準では、小文字とアンダースコアのみが使用されています。
于 2012-04-18T06:01:12.090 に答える
5

このコードは、「ルーティングレコード」を「コード」でグループ化しています。


%Routingsハッシュです。それは「コード」によって鍵がかけられます。各値は配列への参照です。これらの配列はpush ( @{ $Routings{$Code} },、の略であるによって自動化されpush ( @{ $Routings{$Code} //= [] },ます。

これらの各配列には、いくつかの「レコード」が含まれています。各「レコード」は、3つの要素(「プロセス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}それらの配列参照の1つに評価されます。上記の例から、

[
   [ $ProcessID0, $setup0, $Process0 ],
   [ $ProcessID2, $setup2, $Process2 ],
   ...
],

@{ ... }その参照を参照解除することをPerlに示します。言い換えれば、それはあなたが配列自体に興味があることをPerlに伝えます。

配列をforeachに渡すと、その要素を繰り返し処理します。したがって、ループを初めて通過する$_と、次の配列参照が保持されます。

[ $ProcessID0, $setup0, $Process0 ],

2回目、

[ $ProcessID2, $setup2, $Process2 ],

@$_[0](の略で@{ $_ }[0]、誤って使用され${ $_ }[0]、読みやすくなります$_->[0])は、参照される配列($ProcessID0)の最初の要素を取得します。同様に@$_[1]@$_[2]get$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");
}

また

print(join(', ', @$_), "\n")
   for @{ $Routings{$Code} };
于 2012-04-18T04:00:26.630 に答える
0

It's not actually trying to return the data -- it is simply creating variables with the data and then promptly doing nothing with the data. Try this:

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

Unless you use the variables within the loop, there's not much point having it.

The complicated-ish @{ $foo{$bar} } construct is telling Perl to treat $foo{$bar} as an array. $foo{$bar} is a hash lookup. (See the %Routings = (); at the top? That declares and initializes the hash.)

Perl is definitely neat but things like this were enough to make me decide to write new code in newer languages such as Ruby. This code might not be significantly nicer in Ruby, either, and someone well-versed in either wouldn't really care, but you might wish to take the opportunity to re-write tools as you need them maintained.

于 2012-04-18T03:15:48.713 に答える