• 締切済み

サブルーチンの返値に配列のハッシュ

いつもお世話になっております。 HTML::Templateを使用する為、サブルーチンの返値に配列のハッシュを入れたいと思っていますが、うまくいきません。 また、myの使い方が分からず、返値までの方法も若干気になります。 よろしくお願い致します。 use HTML::Template; @loop = &loop_make('aa<>ab<>ac','ba<>bb<>bc','ca<>cb<>cc'); my $template = HTML::Template->new(filename=>'sample.html'); $template->param(loop=>\@loop); print "Content-Type: text/html\n\n"; print $template->output; exit; sub loop_make{ @aaa = (); foreach $xxx (@_) { ($one,$two,$three) = split(/<>/,$xxx); push @loop, { one => $one, two => $two, three => $three }; } return \@aaa; }

みんなの回答

  • ralf124c
  • ベストアンサー率52% (232/446)
回答No.5

前のQAでの流れでこちらもよろしくとのことで回答させていただきます。 もうすでにたくさんの方々がキモの部分を回答されているので蛇足になってしまいますが・・・。 スペースの都合でコンパクトにしているのなら申し訳ありませんが、変数の取り扱いについて二言三言。 あくまでソースから受ける印象としてコードを小さくしようとするあまり変数の住み分けが混乱しているように思われます。 まず初期のコーディングでは変数は出来るだけ局所的に定義し、サブルーチンはグローバル変数に依存せずそれ単体で動作 するように作ると、バグがどこにあるのかがわかりやすくなりますし、サブルーチンが他のものに使いまわせるストックと して財産にもなると思います。 また、変数はそれぞれ機能別に用意し、元のデータを壊す(上書き)ことなく取り扱えば、元データを2次3次使用できます。 そうすることで整理がつき問題点がはっきりするのではないかと思います。  「loop_make」ではぱっと見で一番の問題は配列変数「@aaa」を戻り値にしているにも関わらず代入等をそれに対して行っ ていないことから、出口を用意しておいて処理をどう書いてよいのかわからなかったのではないでしょうか。  この場合ハッシュのPUSHを行っている配列「@loop」が「@aaa」がくるべきところなんですよというご指摘が先達よりなさ れております。  また、戻り値を配列のリファレンスに指定しながら、それを別の配列に代入しようとしていることから、こうしたいという 気持ちは伝わるのですが、強引過ぎると思います。メモリー効率の良い処理をさせる姿勢は良いのですがリファレンスは多用 しない方が後々のメンテの為にも良いと思います(もちろん適材適所で臨機応変に)し、限度はありますが今のリソースが豊富 な時代では、ある程度考慮しなくても良いと思います。  結果としてTemplateモジュールでループ処理を行う為には   ・ハッシュのリファレンスを配列に入れ   ・その配列のリファレンスをハッシュに入れて   ・ハッシュのリファレンスをパラメータとしてTempleteモジュールに渡す  と分離した方がそれぞれの役割が整理されわかりやすくなるし、そうすることで縦横の表を作成する2重ループなども応用 させやすくなるかと思います。  Perlはいい加減に書いても動いてしまうことが多いので、しっかりと整理して取り組まないと期待した結果は得られません。  構造化プログラミング教科書風に書くと以下のようなリストになるかと思います。 ## -------------------------------------- use HTML::Template; my @aDT = (  'aa<>ab<>ac',  'ba<>bb<>bc',  'ca<>cb<>cc' ); my %hLoop; my @loop = &loop_make(@aDT); my $hLoop{loop} = \@loop; my $template = HTML::Template->new(filename=>'sample.html'); $template->param(\%hLoop); print "Content-Type: text/html\n\n"; print $template->output; exit; ## -------------------------------------- sub loop_make{  my @aDT = @_;  my @aRT;  my @aItems = ('one','two','three');  foreach(@aDT) {   my @aTmp = split(/<>/);   my %hTmp;   for(0..$#aItems){    my $sKEY = $aItems[$_];    my $sVAL = $aTmp[$_];    $hTmp{$sKEY} = $sVAL;   }   push @aRT, \%hTmp;  }  return @aRT; } ## -------------------------------------- ながながとすいませんでした。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.4

> @japan = &loop_make('ichiro<>men<>30','jiro<>men<>20','saburo<>men<>10'); > ... > @america = &loop_make('terry<>men<>30','andy<>men<>20','Joe<>men<>10'); > .... > sub loop_make { > @aaa = (); > foreach $xxx (@_) { > ($name,$sex,$age) = split(/<>/,$xxx); > push @aaa, { name => $name, sex => $sex, age => $age }; > } > return \@aaa; # ここを retrun @aaa; とする > } loop_make サブルーチンの戻り値を配列で受け取る場合は、return @aaa; とすればうまくいくはずです。 この場合は、その時点での配列の中身をコピーするので @aaa に my 宣言する必要はありません。以下に、 配列のリファレンスで受け渡しをする場合のコードを書いておきますので、参考にしてみてください。 この場合は、@aaa に my 宣言する必要があります。 $japan_ref = &loop_make('ichiro<>men<>30','jiro<>men<>20','saburo<>men<>10'); ... $template->param(japan=>$japan_ref); ... $america_ref = &loop_make('terry<>men<>30','andy<>men<>20','Joe<>men<>10'); ... $template->param(america=>$america_ref); ... sub loop_make { my @aaa = (); foreach $xxx (@_) { ($name,$sex,$age) = split(/<>/,$xxx); push @aaa, { name => $name, sex => $sex, age => $age }; } return \@aaa; }

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

my の動作はちょっと微妙なところがあるんだけど, 基本的には「現在のブロックを有効範囲とする変数を作る」ということで OK です. 「有効範囲」には「変数名の有効範囲 (可視範囲)」と「値の有効範囲」があるのですが, my ではどちらも現在のブロック (の my による宣言以降) に限定されます. つまり, 他のプログラム言語における「ローカル変数」と同じような使い方ができます. もうちょっと言うと, my によって「現在のブロックを可視範囲とする新たな変数を作り, その変数が値を保持するための領域を新しく確保する」という動作をします. このときに, 新しく確保する領域は「プログラムのどこからも参照できない」ことが保証されます. これは「プログラムから参照できるところには確保されない」とも言えます. #2 の下の例のようなプログラムでは 1. foreach ループの最初の繰り返しで %aaa に新しい領域を確保する 2. その後, %aaa に対するリファレンスを @loop に push する 3. 次の繰り返しで %aaa に領域を確保する という動作になるわけですが, 1 で確保した領域は 2 により @loop から参照できるので, 3 で確保する領域は 1 で確保した領域とは異なることが保証されます. C などでは「同じ領域を使いまわすかもしれないので局所変数へのポインタを返してはいけない」とされていますが, Perl では「my 宣言した変数へのリファレンスを返す」ことは全く正当ですし一般的に行われています. 逆に, my を使わないと同じ領域であることが保証されるので期待した動作をしません. なお, 「my で宣言した変数の可視範囲はそのブロックに限定される」ことと「ブロックの中でサブルーチンを定義してもよい (その場合でもサブルーチン名は現在のパッケージ全体で有効)」ことを使うと, C でいう「static な局所変数」や「いくつかのサブルーチンからのみアクセスできる変数」も容易に作ることができます.

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.2

> また、myの使い方が分からず、返値までの方法も若干気になります。 loop_make サブルーチンの中で @loop 配列を作成しているので、この場合は戻り値は必要ありません。 次のようにすると、うまくいくと思います。 &loop_make('aa<>ab<>ac','ba<>bb<>bc','ca<>cb<>cc'); ... sub loop_make { foreach $xxx (@_) { ($one,$two,$three) = split(/<>/,$xxx); push @loop, { one => $one, two => $two, three => $three }; } } my は、変数のスコープを制限する役割があります。上のサブルーチンでハッシュを使う場合は、my が必須となります。 sub loop_make { foreach $xxx (@_) { ($one, $two, $three) = split(/<>/,$xxx); my %aaa; # my が必須 $aaa{one} = $one; $aaa{two} = $two; $aaa{three} = $three; push @loop, \%aaa; } }

sfgfsherg
質問者

補足

ご返信ありがとうございます。 申し訳御座いません。 push @loop, { one => $one, two => $two, three => $three }; ↓↓↓↓↓ push @aaa, { one => $one, two => $two, three => $three }; の間違いでした。 <TMPL_LOOP NAME="loop"> のようにループ名の固定、またはサブルーチン外で名前を変える、これでなんとなく解決できたような気がしますが、サブルーチン外のことなので少しひっかかります。 リファレンス・デリファレンスなどで多重配列のreturnはできませんでしょうか? 宜しくお願い致します。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

「配列のハッシュ」って何を意味するものなんでしょうか? 配列のリファレンスを値に持つハッシュ? loop_make の foreach ループ中で @loop に値を入れているにもかかわらず, 最後に \@aaa と @aaa のリファレンスを返しているのはなぜ? しかもその返り値を @loop = ... のように配列に入れていいの?

sfgfsherg
質問者

補足

ご返信ありがとうございます。 申し訳御座いません。 push @loop, { one => $one, two => $two, three => $three }; ↓↓↓↓↓ push @aaa, { one => $one, two => $two, three => $three }; の間違いでした。 出来る限り簡単にと思いすぎて、少し質問がおかしくなっていました。 やりたいことは下記のようなことです。 ■japan_template.html <TMPL_LOOP NAME="japan"> <TMPL_VAR NAME="name"><TMPL_VAR NAME="sex"><TMPL_VAR NAME="age"><BR> </TMPL_LOOP> ■america_template.html <TMPL_LOOP NAME="america"> <TMPL_VAR NAME="name"><TMPL_VAR NAME="sex"><TMPL_VAR NAME="age"><BR> </TMPL_LOOP> use HTML::Template; { @japan = &loop_make('ichiro<>men<>30','jiro<>men<>20','saburo<>men<>10'); my $template = HTML::Template->new(filename=>'japan_template.html'); $template->param(japan=>\@japan); open(FH, ">japan.html"); print FH $template->output; close(FH); } { @america = &loop_make('terry<>men<>30','andy<>men<>20','Joe<>men<>10'); my $template = HTML::Template->new(filename=>'america_template.html'); $template->param(america=>\@america); open(FH, ">america.html"); print FH $template->output; close(FH); } exit; sub loop_make{ @aaa = (); foreach $xxx (@_) { ($name,$sex,$age) = split(/<>/,$xxx); push @aaa, { name => $name, sex => $sex, age => $age }; } return \@aaa; } サブルーチン内でmyを使おうと思いましたが、使い方がわからず、結局 @aaa = (); で一旦クリアすることにしたのですが、なにかスマートでは無い気がします。 return \@aaa; の所ですが、リファレンスなどのことも調べましたが、結局解決できずでした。 やはりreturnするよりもサブルーチン外にpushした配列を固定して使用するしかないのでしょうか? 宜しくお願い致します。

関連するQ&A