• 締切済み

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

いつもお世話になっております。 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; }

  • Perl
  • 回答数5
  • ありがとう数1

みんなの回答

  • 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

  • HTML::Templateのループと配列のハッシュの作成

    下記の?????の所にpushで配列のハッシュを作成しようと考えていましたが、うまくいきません。 下記のソース自体が的外れでしたら申し訳ございません。?????以外の場所も指摘して頂ければ幸いです。 よろしくお願い致します。 <TMPL_LOOP NAME="loop"> <TMPL_VAR NAME="one"><BR> <TMPL_VAR NAME="two"><BR> <TMPL_VAR NAME="three"><BR> </TMPL_LOOP> use HTML::Template; @data = ('aa<>ab<>ac','ba<>bb<>bc','ca<>cb<>cc'); foreach $xxx (@data) { my ($one,$two,$three) = split(/<>/,$xxx); ???????????????????? } my $template = HTML::Template->new(filename=>'sample.html'); $template->param(loop=>\@loop); print "Content-Type: text/html\n\n"; print $template->output;

    • ベストアンサー
    • Perl
  • ハッシュリファレンスの未定義

    サブルーチン/ハッシュリ/ファレンスで悩んでいます。 my (@r); $r[1]{"A"} = "1-A"; # 代入 &s(\@r); print $r[1]{"A"},"\n"; # 参照 print $r[2]{"B"},"\n"; # 参照 サブルーチン側でできない。 # sub s() { my ($c)=@_; @$c[1]->{"A"} = "1111-AAAA"; # もちろん代入できる @$c[2]->{"B"} = "2-B"; # 代入 これができない(ハッシュリファレンスの未定義エラー) } サブルーチン側で新規ハッシュのところに代入ができないのですが どのようにすればいいのでしょうか。

    • ベストアンサー
    • Perl
  • サブルーチンへ渡した配列のリファレンスをデリファレンスするのが面倒なのですが。。

    MAIN: {   my @array = (1 .. 5);   print three(\@array);   exit; } sub three {   my $array = shift;   return $$array[2]; } のように、サブルーチンに配列リファレンスを渡したあと、$$array[2]のようにデリファレンスするのが面倒なのですが、 このとき$array[2]と書ける何か良い方法はないでしょうか? # 大きな配列を取り扱うので、リファレンスを使いたいんです。。 型グロブを使うことも考えたのですが、サブルーチン内でmyで宣言出来なくて、スコープ的にまずくなりそうなので断念しました; どなたかアドバイスしていただけると嬉しいです。。

    • ベストアンサー
    • Perl
  • ハッシュに保存した配列を取得する方法

    ハッシュに保存した配列を取り出すサンプルを2ケース作成したのですがいずれもうまくいきませんでした。 1ケース目は「value=sample2」、2ケース目は「value=5」と表示されてしまいました。 いずれも@array2の内容が表示されることを期待していたのですがどのように修正すればよろしいでしょうか? my @array1 = (0, 1, 2, 3, 4); my @array2 = (5, 6, 7, 8, 9); # 1ケース目_ハッシュを使用 my %total = ( sample1 => @array1, sample2 => @array2 ); # 結果表示1 my @res = %total{sample2}; for (my $i = 0; $i < @res; $i++) { print 'value=' . $res[$1] . "\n"; } # 2ケース目_ハッシュのリファレンスを使用 my $total2 = { sample1 => @array1, sample2 => @array2 }; # 結果表示2 my @res2 = $total2->{sample2}; for (my $i = 0; $i < @res2; $i++) { print 'value=' . $res2[$1] . "\n"; }

    • ベストアンサー
    • Perl
  • リファレンスをサブルーチンの戻り値にしてもOKですか?

    ■ サブルーチン内部で処理した結果を格納した、配列、ハッシュ、スカラーなどのデータを戻り値として利用する必要があります。その場合、どうするのが標準的なやり方でしょうか? ■ return (配列へのリファレンス, ハッシュへのリファレンス, スカラー); などとやってしまっても問題はないでしょうか? ■ 下のプログラムを試したところ、予想に反しちゃんと 「31415」と表示されました。 #!/usr/bin/perl -w sub subroutine{ my @a = (3, 1, 4, 1, 5); return \@a; } my $b = subroutine{}; print @$b; ■ サブルーチン内部で使用した変数へのリファレンスをサブルーチン 外で使っていいのだろうか? サブルーチンの処理が終了した時点でサブルーチン内部で使用した 変数はメモリーから消去されるのかと思ったものですから。

    • ベストアンサー
    • Perl
  • HTML::Templateの初歩的な質問

    HTML::Templateを使用してサイトを構築しようとしてるのですが、うまくいきません。 例1のようにしたいのですが、どこが間違っているかを指摘していただきたく書き込ませて頂きました。 ちなみに例2ではうまく作動しています。 よろしくお願い致します。 サンプルコード ★sample.html★ <TMPL_LOOP name=arr> <p><TMPL_VAR name=val></p> </TMPL_LOOP> ★例1★ my $template = HTML::Template->new(filename => 'sample.html'); @arr = [ {val=>'1'},{val=>'2'},{val=>'3'} ]; $template->param(arr => \@arr); print "Content-Type: text/html\n\n"; print $template->output; ★例2★ my $template = HTML::Template->new(filename => 'sample.html'); $template->param(arr => [ {val=>'1'},{val=>'2'},{val=>'3'} ] ); print "Content-Type: text/html\n\n"; print $template->output;

    • ベストアンサー
    • Perl
  • HTML::Templateでカレンダー出力したい

    Calendar::SimpleとHTML::Templateを利用しtableタグを使ったカレンダー表示を行いたいのですが、1週間ごとに<tr>タグで区切るループの記載の仕方がわかりません。 見本にした他所様のbaseサブルーチンでは1週間ごとに<tr>タグで区切られたきれいなカレンダー表示がされますが、myplanサブルーチンでは現状<tr>タグで区切る部分が作られていないため、日にちの数字が1週間ごとに区切られずにずらっと横並びとなってしまっている状態です。 Calendar::Simple http://perldoc.jp/docs/modules/Calendar-Simple-1.07/Simple.pod HTML::Template http://perldoc.jp/docs/modules/HTML-Template-2.6/HTML/Template.pod baseでの表示 日 月 火 水 木 金 土     1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 現状のmyplanでの表示 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 use HTML::Template; use Calendar::Simple; local $cal = calendar; local @caps = qw( 日 月 火 水 木 金 土 ); local @wdays = qw( sunday monday tuesday wednesday thursday friday saturday ); #&base; &myplan; exit; sub base{ print "Content-type: text/html; charset=\"utf8\"\n\n"; print qq|<table>\n|; print qq|<tr>\n|; for (my $i = 0; $i < 7; $i++) { my $cap = $caps[$i]; my $wday = $wdays[$i]; print qq|<th class="$wday">$cap</th>\n|; } print qq|</tr>\n|; foreach my $week ( @$cal ) { print qq|<tr>\n|; for (my $i = 0; $i < 7; $i++) { my $mday = $week->[$i]; my $wday = $wdays[$i]; print qq|<td class="$wday">$mday</td>\n|; } print qq|</tr>\n|; } print qq|</table>\n|; }#end base sub myplan{ for (my $i = 0; $i < 7; $i++) { my %row_data; $row_data{cap} = $caps[$i]; $row_data{wday} = $wdays[$i]; push(@loop_data, \%row_data); } foreach my $week ( @$cal ) { for (my $i = 0; $i < 7; $i++) { my %row_data; $row_data{mday} = $week->[$i]; $row_data{wday} = $wdays[$i]; push(@loop_data_3, \%row_data); } } my $template = HTML::Template->new( filename => "html_template/calender.tmpl", ); $template->param( THIS_LOOP => \@loop_data, THIS_LOOP3 => \@loop_data_3 ); print "Content-type: text/html; charset=\"utf8\"\n\n"; print $template->output; }#end myplan ##calender.tmpl <html> <body> <table> <tr> <TMPL_LOOP NAME=THIS_LOOP> <th class="<TMPL_VAR NAME=wday>"><TMPL_VAR NAME=cap></th> </TMPL_LOOP> </tr> <tr> <TMPL_LOOP NAME=THIS_LOOP3> <td class="<TMPL_VAR NAME=wday>"><TMPL_VAR NAME=mday></td> </TMPL_LOOP> </tr> </table> </body> </html>

    • ベストアンサー
    • Perl
  • perlでサブルーチンへの複数の配列渡し

    perlでサブルーチンに配列を渡しているのですが、 引数としている配列が1個の場合は問題ないのですが、 複数渡すと、第2引数以降が渡りません。 どの様にすれば上手くできますか。 例1 #!/usr/bin/perl @x1 = (14, 11, 5, 12, 8, 15); @x2 = (12, 10, 8, 9); print "main : @x1 \n"; &test(@x1); sub test { my (@arg1) = @_; print "sub : @arg1 \n"; } 結果 main : 14 11 5 12 8 15 sub : 14 11 5 12 8 15 例2 #!/usr/bin/perl @x1 = (14, 11, 5, 12, 8, 15); @x2 = (12, 10, 8, 9); print "main : @x1 \n"; print "main : @x2 \n"; &test(@x1, @x2); sub test { my (@arg1, @arg2) = @_; print "sub : @arg1 \n"; print "sub : @arg2 \n"; } 結果 main : 14 11 5 12 8 15 main : 12 10 8 9 sub : 14 11 5 12 8 15 12 10 8 9 sub : 引数1に全てが設定されて、引数2に設定されていない。

    • ベストアンサー
    • Perl
  • サブルーティンの使い方。

    サブルーティンの理解を深めるために、 階乗の計算をサブルーティンで行うプログラムを作りました。 自作のプログラムについて質問を二つしたいと思います。 1.一応、計算は出来るのですが、 定型的でないというか、無駄が多いというか、 何か違う気がするのです。 どこか変なところはありませんでしょうか? 2.エラーメッセージをどこにいれたらいいのかわかりません。 数字以外、(例えば文字)が入力されれば1が出力されるようにはしたのですが、 「これは数字ではありません」のようなエラーメッセージを出したいのです。 この場合はどこにどのように記述すればいいですか? 色々試してみたのですが、思い通りに動きませんでした。 みなさま、知恵をお貸しください。 ------------------------------------------- use strict; print "数字を入力してください。\n"; chomp( my $number = <STDIN> ); my $k_number = kaijo($number); print "入力された数字の階乗は$k_numberです。\n"; sub kaijo { my $number = shift @_; return undef if $number < 0; return 1 if $number == 0; my $kaijo = 1; for(my $i = $number; $i>1; $i--) { $kaijo *= $i; } return $kaijo; }

  • 配列を引数とするサブルーチンに関する質問

    配列を引数とするサブルーチンに関する質問です。 Trimと Hex2Decのサブルーチンをインターネットを参考に作りました。 下記のスクリプトは > perl test.pl で期待通りに動きます。 質問は、サブルーチンの中における、 for (@out) { $_=hex; } です。 trimの方では、$_がなくても、きちんと動きます。(あってもOK、$_=~s/^\s+//; $_=~s/\s+$//;) Hex2Decの方は、$_=hex; でないと動きません。 これは、for (@out) { }でひとつずつ処理する対象が、$_に入っている。 処理結果の格納先が、s/^\s+//; の場合は、記述無き時は、 $_と暗黙できまっているのに対し、 hex;の場合は、そうではない。明示的に与えてやらなければいけない。 こういう理解で、良いでしょうか? 他に、やりようはあるのでしょうか? 宜しくお願いいたします。 test.pl ----------------------------------------- $a=10,print "Hex=".$a." --> Dec=".&Hex2Dec($a)."\n"; $a=20,print "Hex=".$a." --> Dec=".&Hex2Dec($a)."\n"; @a=('a','b','c','d'); @b=&Hex2Dec(@a); print "Hex=@a --> Dec=@b\n"; $a=' a ',print "org=$a --> trimed =".&trim($a)."xxxxx\n"; $a=' b ',print "org=$a --> trimed =".&trim($a)."xxxxx\n"; @a=(' a ',' a ',' a ',' a '); @b=&trim(@a); print "org=@a --> trimed= @b\n"; sub trim { my @out = @_; for (@out) { s/^\s+//; s/\s+$//; } return wantarray ? @out : $out[0]; } sub Hex2Dec { my @out = @_; for (@out) { $_=hex; } return wantarray ? @out : $out[0]; } ------------------------------------------------

    • ベストアンサー
    • Perl

専門家に質問してみよう