• 締切済み

Perlのサブルーチンの引数に配列やハッシュをCall by Valueすることはできない?

 タイトルどおりなんですが・・・。  Perlのサブルーチンに配列やハッシュを実体渡ししようとしたんですが,どうやってもできません.あきらめて,参照渡しにしたら,なんかうまくいっている模様です.  これは,そもそも,はじめから参照渡ししか用意されていない、ということでしょうか?.それとも,何か実体渡しする方法があるのでしょうか?.  後,余談なんですが,配列やハッシュの要素に別の配列やハッシュの実体を入れることはできるのですか?.これもちょっと試してみたらできなさそうだったのですが,あちこちのWebにある入門マニュアルみたいなページを見ても,その辺の話が「できる」とも「できない」ともほとんどの場合かいてなくて,自分の試し方が悪いのか,それとも無駄なことを延々とやっているのかがわからなくて疲れます.

  • Perl
  • 回答数6
  • ありがとう数9

みんなの回答

  • leaz024
  • ベストアンサー率75% (398/526)
回答No.6

■パラメータ渡しの仕組みについて サブルーチンへのパラメータの渡され方ですが、まず、呼出側で指定したパラメータは1つのリストに展開されます。 そしてサブルーチン側では、そのリストの「別名配列」として @_ が用意されます。 @_ はパラメータリストの別名なので、$_[n] を操作することは、パラメータに指定した変数を直接操作していることと同じになります。 つまりPerlでは、常に「別名渡し」が行われます。 (即値やハッシュのキーのように別名が作れないものは、一時領域に値がコピーされ、その別名が渡るようです。) ちなみにパラメータが即値である場合、変更しようとするとエラーになります。 ■「値渡し」と「リファレンス渡し」について サブルーチン側でのパラメータの操作を、呼出側の変数に反映させたくない場合は、パラメータを「値渡し」にします。 Perlでの値渡しは、サブルーチン側で用意した my(または local)変数に @_ の値をコピーすることで実現します。 また、呼出側の変数の値を操作したい場合でも、$_[n] の内容を直接編集することはあまりなく、普通はリファレンスを渡し、それをデリファレンスして値を操作します。 # 「実体渡し」とは、何が渡ることを望んでいるのでしょうか? ■複数の配列やハッシュの渡し方 パラメータは、全てが1つのリストに展開されるため、複数の配列やハッシュを渡したい場合は注意が必要です。 例えば、次のように2つの配列を渡しても、   @x = qw(abc def);   @y = (1, 2, 3);   &f2(@x, @y);   sub f2 {     my (@a, @b) = @_;   # 2つの配列が欲しいのでこうしたが・・・     print "a:[@a]\n";     print "b:[@b]\n";   }  実行結果:   a:[abc def 1 2 3]   b:[] 1つ目の配列に、全て入ってしまいます。 これを回避するには、リファレンスを渡します。   &f3(\@x, \@y);   sub f3 {     my ($a, $b) = @_;   # リファレンスはスカラーなので、$変数で受け取る。     print "a:[@$a]\n";     print "b:[@$b]\n";   }  実行結果:   a:[abc def]   b:[1 2 3] ■サブルーチンからの戻値について サブルーチンからの戻値は、呼出時のコンテキストに依存します。 例えば、配列やハッシュ、リストを返していても、$変数で受け取っていたり、演算式に組み込んだりすると、それぞれに応じたスカラー値が返ります。 それ以外では、基本的にはリスト値が返ります。(配列やハッシュも、リストに展開されて返されます。) どちらの場合でも、勝手に別名やリファレンスが返されることはありません。戻値は常に値渡しです。 # ハッシュは return できない、とありましたが、もちろんできます。 ■配列とリストについて とかく同じ物と思われがちですが、これらは別物です。 例えば、それぞれをスカラーコンテキストで参照すると、配列は「要素数」が返るのに対し、リストでは「最後の要素」が返ります。 また、当然ながらリストには変数領域がないので、暗黙に配列へのリファレンスを引数に取る push 関数などには、リストは渡せません。 リストはあくまで、その場限りのものです。 ■無名配列、無名ハッシュについて これらは、変数の必要なしに、配列やハッシュの中身をメモリ上に生成するものです。 無名配列は   [ リスト ] 無名ハッシュは { リスト } として、生成します。 これらの値は、それぞれ「配列へのリファレンス」「ハッシュへのリファレンス」です。 > 配列やハッシュの要素に、別の配列やハッシュを入れる というのは、この無名**を使います。(つまり、配列などの実体は入れられません。) 例えば、4×3の2次元配列は、次のように作ります。   my @a = ( [0,1,2,3],[4,5,6,7],[qw(a b c d)] ); 各要素を見るときは、   foreach $row (@a) {     foreach $val (@$row) {       print "$val ";     }     print "\n";   } とか、   foreach $r (0..2) {     foreach $c (0..3) {       print "$a[$r][$c] ";     }     print "\n";   } とします。 ■No.4 の補足について タイプミスかもしれませんが、   sub subroutine {     my $sch = $_[0];     my $arr = @{ $_[1] };     my $hsh = %{ $_[2] };   } これではダメです。 やるなら、   sub subroutine {     my $sch = $_[0];     my @arr = @{ $_[1] };     my %hsh = %{ $_[2] };     $arr[0] = …     $hsh{AAA} = …   } とするか、   sub subroutine {     my $sch = $_[0];     my $arr = $_[1];     my $hsh = $_[2];     $arr->[0] = …     $hsh->{AAA} = …   } とします。 分からない部分は補足してください。

  • haporun
  • ベストアンサー率40% (230/562)
回答No.5

私が言ってる参照とリファレンスは別のものです。 リファレンスはそれ相当の構文が必要ですが、参照は暗黙のうちに参照先メモリ領域に代入します。 参照はC++で int& x = variable; と宣言したのと同じ、でリファレンスはCのポインタと同じで int *y = &variable; と宣言したのと同じです。 Cがわかんなかったらごめんなさい。 hoge($a); sub hoge{ $_[0] = "ほげ"; } のように、リファレンスの構文を使わなくても、暗黙に$aに代入されています。 もちろんリファレンスを使えば、配列やハッシュの変数も渡せますが、それでは構文が変わってしまうので、痛いと思ったのです。 >return %hash; をすると、どうもリファレンスが返されてるような。 ちなみにリストと配列は同じものだと思います。 >それぞれへのリファレンスではなく,コピー実体が入ってそうな コピー実態ではなく暗黙参照が入っています。 その変数をそのままプリントしても、アドレスが出るわけではなく、変数の値が表示されます。 しかし、その変数への代入は、参照先の変数を書き換えることになるのです。 そして、これができるのがサブルーチンスコープ内だけだということです。 return @_;して、それをサブルーチン外で受け取れば、参照が残っているような気もしますが、外に出たとたん参照は切れてしまいます。 $a = 5; @ret = hoge($a); print $a; # 10; $ret[0] = 100; # 参照は切れてるので影響なし。 print $a; # 10 sub hoge{ $_[0] *= 2; return @_; }

  • haporun
  • ベストアンサー率40% (230/562)
回答No.4

byval(@a)と書くと($a[0..$#a])を渡したのと同じになります。 リファは入ってません。 しかし、@_は@aの参照ではありません。 $_[0]から$_[$#a]が$a[0]から$a[$#a]の参照なのです。 だから内部で@_=@otherとか代入しても、関係ないです。 また、それぞれが参照である期間は、サブルーチンスコープ内のみです。 これはVisual BasicやJAVAなど、参照で引数を取るタイプの言語としては標準の動きみたいです。 ちなみに自作サブルーチンに渡す引数は、全部リスト扱いです。 (@a, 5, 6, 'ほげ')とかリスト表現できるように、それをサブルーチンに渡してるだけです。 ちなみにbyval(%hash)したら、さすがにハッシュリファが渡ってしまいました。 また、ハッシュ自体をreturnできそうもないので、これもまた考え物です。

stanaka
質問者

お礼

 まず,ありがとうございます(笑).  補足を書いてから気がついたので,ここに書かせていただきます.  ひょっとして,サブルーチンの引数に配列を指定すると,(引数はリストコンテキストなので)配列がリストに展開されて,配列の要素分の引数がサブルーチンに引き渡される,と言うことですか?.とすると, > $_[0]から$_[$#a]が$a[0]から$a[$#a]の > 参照なのです。  とはいいますが,$_[0]から$_[$#a]には$a[0]から$a[$#a]それぞれへのリファレンスではなく,コピー実体が入ってそうな気がするんですが・・・.  ちょっと今,実験できる環境ではないので,帰ってから実験してみます.

stanaka
質問者

補足

 がーん.  えーっと,俺様なんだか激しくマチガイ理解の予感が・・・.  ちょっ,ちょっとまってくださいね.  今,僕は, sub subroutine{  my $sch = $_[0];  my $arr = @{ $_[1] };  my $hsh = %{ $_[2] }; }  とやっておけば, &subroutine( $sch, \@arr, \%hsh );  とやったときに,ローカルスコープの変数へそれぞれちゃんとコピーできるのではないか,と理解しているのですが,これは間違っていますか?.  それと,ハッシュをreturn出来ない,と言うのは,サブルーチン内で  return %hsh;  と言うのが出来ない,と言うことですか?.  また,僕はいまいちリストコンテキストと言う物が理解できなかったのですが,ここを読んでるうちに一つ思ったことがあります.次のような物ですが,これはまた間違っているでしょうか?,またどこが間違っているでしょうか?.  つまり,データ構造としては「リスト」,「配列」,「ハッシュ」があり,それぞれ, ( e1, e2, e3 ); [ e1, e2, e3 ]; ( 'k1' => e1, 'k2' => e2, 'k3' => e3 );  のような記法で表現できる.ここで,@変数にリストの記法で代入を行うと,配列が生成され,%変数にリストの記法で代入を行うと,偶数番要素がキー,奇数番要素が要素となるハッシュが生成される.  同様にリストの記法の左辺に@変数を代入すると,右辺の配列の要素と同じ値を要素とするリストが生成され,代入される.  分けわかんなくなって来てますが,よろしくお願いします.

  • sssohei
  • ベストアンサー率33% (33/98)
回答No.3

英語なんですが perldoc perldsc とすれば全パターンのってます^^ (配列の配列、配列のハッシュ、ハッシュの配列、ハッシュのハッシュ)×(静的、動的) 駱駝本(ProgrammingPerl)第2版の4.7に翻訳があります。 Hash Of Hashであれば、 # print the whole thing foreach $family ( keys %HoH ) { print "$family: { "; for $role ( keys %{ $HoH{$family} } ) { print "$role=$HoH{$family}{$role} "; } print "}\n"; } こんな感じにやるようです>perldscからの引用です。 書くと大変なので、一度読んでみてください^^ 横やりですが >出来ないはずの配列の実体渡しという たぶん、元の配列のコピー(テンポラリ)への参照なので、「一見実体渡しのように見える参照渡し」では無いでしょうか?(推測) トリッキーな印象を受けるので、先頭でコピーする方が一般的で良いんじゃないでしょうか^^ sub hoge { my ($arg1, $arg2, ... , $argN) = @_; ... }

  • haporun
  • ベストアンサー率40% (230/562)
回答No.2

関数の戻り値や式の結果は値渡しされます。 @a = (5, 6, 7, 8, 9); print "初期値\n"; print '@a = '; print "$_ " foreach(@a); print "\n"; hoge(byval(@a)); print "hoge(byval(\@a));しました\n"; print '@a = '; print "$_ " foreach(@a); print "\n"; hoge(@a); print "hoge(\@a);しました\n"; print '@a = '; print "$_ " foreach(@a); print "\n"; sub hoge($){ $_ *= 2 foreach @_; } sub byval{ return @_; } Visual Basicみたいな構文になりました。 動作環境: W2K+ActivePerl5.6 えーと、ハッシュの場合は考えてないです。 応用してください。

stanaka
質問者

補足

 なるほど,返り値はコピー実体なので,引数をそのまま返す関数を作って,ワンクッションおくと言うことですか?.  しかし,書いていただいたソースがよく分らなかったのですが,byval()関数が返す実体は,先頭要素に与えた配列へのリファレンスが入っているような配列になりませんか?.  しかもそのままそれをhoge()関数に与えると,出来ないはずの配列の実体渡しという事になりませんか?.

  • sssohei
  • ベストアンサー率33% (33/98)
回答No.1

 最新のPerlはわかりませんが、(少なくとも初期の)Perl5では参照渡しか出来ないはずです。  駱駝本第2版「2.7 サブルーチン」を読んだ限り、「my で宣言されたローカル変数に、コピーすることで値渡しを実現する」ようです。 >配列やハッシュの要素に別の配列やハッシュの実体 できます。 とりあえず、静的には @AoA = ( # 配列の配列 ["foo1", "bar1"], ["foo2", "bar2"], ["foo3", "bar3"], ); やら %HoA = ( # ハッシュに配列 first => ["foo1", "bar1"], second => ["foo2", "bar2"], third => ["foo3", "bar3"], ); でできます。[LIST] のような形で配列が得られるようです。

stanaka
質問者

補足

 早速の御回答ありがとうございます.  昨日確かに補足を投稿したと思うのですが,消えてなくなっています.おかしいと思いつつもう一度投稿させていただきます.  即値を代入できることは分りました.ありがとうございます.また,これをアクセスする方法もいろいろやってるとわかりました.  ただ,次のような代入が可能なのかどうか教えて欲しいです.  @AoH = ( %hash1, %hash2 );  あるいは,この逆など(%HoAや%HoHへの代入)は可能なのでしょうか?.  また,可能であるとして,どの様にアクセスすればいいのでしょうか?.とりあえず,代入の時点でエラーは出ないのですが,参照しようとしても,  foreach $key ( keys( %HoH ) ){  のなかでは,   %H = $HoH{$key};  としても,   %H = %{ $HoH{$key} };  などとしても参照できませんでした.

関連するQ&A

  • Perlのクラス(*.pm)からuseする側の*.cgi内ののスカラやハッシュ、配列の参照は可能でしょうか?

     Perlのクラス(*.pm)からuseする側の*.cgi内ののスカラやハッシュ、配列の参照は可能でしょうか? いろいろなサーバに対応させやすくするため 1行目(Perlのパス)や各種モジュールのパスや設定情報を*.cgiに書いてプログラム本体は*pmに置きたいのです。 それとももっといい方法がございますでしょうか。  教えて!ください。

    • ベストアンサー
    • 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
  • サブルーチンの引数としてハッシュを渡したい

    ハッシュを引数として受け取り、そのハッシュの内容を csv形式に変換し出力するというサブルーチンを作っています。 ハッシュのキー名は固定なのですが、 引数として渡すハッシュの名前がバラバラの場合、 それを引数としてうけとることは可能なのでしょうか? また、どのように受け取ればいいでしょうか?

    • ベストアンサー
    • Perl
  • Perl・DBIでの汎用selectサブルーチンを作成するには?

    Perl・DBIで、select文発行のサブルーチンを作成していますが、未熟なため、完成できません。 呼び出し側では、下の記述のように、サブルーチンの引数として、SQL文を渡し(SELECT系)、戻り値として、SELECTされた結果を取得したい(配列もしくは、ハッシュで)と思っております。 もし、参考にできる、サイト・ サンプルサイト等のご紹介でも、結構ですので、ご教授くださればと思います。(処理の流れ等の箇条書きでもかまいません。) -------------------------------------------- # 呼び出し側(メインルーチン) $name;#テンプレート置換変数  $age; #テンプレート置換変数 my $sql =<<"EOQ"; # この抽出select文はその都度変わる。 select NAME,AGE from EMP EOQ my @row = &do_select($sql); $name = $row[0]; $age = $row[1]; もしくは、 $name = $hash_ref->{NAME}; $age = $hash_ref->{AGE}; のような記述で代入。 # テンプレートを読み込んで、置換処理で代入 ※幾分省略しています。 <table> for(){# SELECT分の結果の行数分繰り返し処理。 <tr>  <td>$name</td>  <td>$age</td> </tr> } </table> my $dbh = DBI->connect($dbhost, $dbuser, $dbpass) || die $dbh::errorstr; ##### select発行サブルーチン ####### sub do_select{ my $sth; my $rv; my @row; # 戻り値(フィールドが入る) my %dbhash; # 戻り値はこちらかな? my $sql = shift(@_); # select文 $sth = $dbh->prepare("$sql"); $rv = $sth->execute || die $sth->errstr; while($hash_ref = $sth->fetchrow_hashref){      # print "$hash_ref->{TITLE}<br>"; # ここに、何かの処理が入る } return @row; }

    • ベストアンサー
    • Perl
  • <Perl>参照配列の出力に失敗する。

    <Perl>参照配列の出力に失敗する。 お世話になります。 配列の出力部で以下のエラーが出力されます。 Use of uninitialized value in print at test2.pl line 12. -----コーディングは以下の通りです。----- #!C:\perl use strict; use warnings; my @l = (); #----------- #GetDataへCSVファイル名と、格納用配列を渡す #----------- my $cnt = &GetData("test.csv", \@l); print "COUNT -> ".$cnt; for(my $i=0; $i < $cnt; $i++){ print $l[$i]; } ################################################################## # 概   要:指定したCSVファイルをオープンしCSVデータを配列に取得する。 # パラメータ:ファイル名, CSVデータ格納用配列 # 戻 り 値:データ取得件数 ################################################################## sub GetData { my ($f, @bf) = @_; my $rcnt = 0; print "FILE NAME -> ".$f."\n"; if ( open(FP, "<${f}") ){ print "FILE OPEN -> success.\n"; @bf = split(/,/, <FP>); close(FP); $rcnt = @bf; print "CSV GET COUNT -> ".$rcnt."\n"; } return $rcnt; } -----実行結果は以下の通りです。----- D:\>perl test.pl FILE NAME -> test.csv FILE OPEN -> success. CSV GET COUNT -> 5 Use of uninitialized value in print at test2.pl line 12. Use of uninitialized value in print at test2.pl line 12. Use of uninitialized value in print at test2.pl line 12. Use of uninitialized value in print at test2.pl line 12. COUNT -> 5 -----CSVファイルの内容は以下の通りです。(ファイル名:test.csv)----- あいうえお,かきくけこ,さしすせそ,たちつてと,なにぬねの 配列の要素数が取れているので、配列内にデータは格納されているとは思っています。 出力方法をどのように正せばよいがご教示お願い致します。

    • ベストアンサー
    • Perl
  • コンパイラの作成

     皆さんのお力を貸してください.  現在, Java 言語を用いてコンパイラを作成しているのですが, 関数の引数として 1 次元配列を渡す方法 ( 配列の要素全体のコピーを渡すのではなく配列の先頭の番地を渡す方法 ) と参照渡しのプログラム記述方法が解らず悩んでおります.  何卒ご教授お願いいたします.

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

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

  • ハッシュのリファレンスを用いた処理

    ActivePerl 5.8 , WinXP SP2の環境です。 Perl スクリプトを用い、ファイルから複数のブロックからなる情報をよみとり、個別のハッシュを作り、それをリファレンスの配列としてまとめて後から参照するという操作をしたいのですが、詰まってしまいました。。 例として読み取るファイルは ---input.txt--------- >1 Jan 1 Feb 4 >2 Mar 9 Apr 3 >3 Oct 8 Nov 4 ------------------ ここから1,2,3の個別のハッシュ {Jan => 1 Feb => 4} {Mar=> 9 Apr =>3} {Oct =>8 Nov =>4} を作成し、それぞれのハッシュのリファレンスの配列をつくり、その後からすべてのハッシュの中身を個別に出力させたいと思いました。 次のようなスクリプトを作成したのですが思ったように作動しません。 use strict; open (IN, "input.txt") or die ("cant open file \n"); my $reff; my @array_of_reff; my %hash; my $count = 0; while(<IN>){ my $line = $_; ######ここでは各ブロックの頭の ">"を認識し、2個目以降であれば直前までで作ったハッシュのリファレンス($reff)を配列@array_of_reffに入れる。 if($line =~ /^>/){ if($count >0){ $reff = \%hash ; push (@array_of_reff, $reff); %hash = (); } $count++; } ########ここではアルファベットが入った行を認識して、ハッシュに追加しています if($line =~ /^[A-Za-z]/){           $line =~ /([A-Za-z]+)\s+/; my $month = $1; $line =~ /\s+(\d+)/; my $day= $1; $hash{$month} = $day;     } ###ここはファイルの最後になったら直前まで作っていたハッシュののリファレンス($reff)を配列@array_of_reffに入れる。 if( eof ){ $reff = \%hash ; push (@array_of_reff, $reff); } } #####ハッシュのリファレンスの配列(@array_of_reff)からもとのハッシュを参照し、ハッシュごとに出力 foreach my $reff_of_hash (@array_of_reff){    print "output";    while( (my $key,my $value) = each %$reff_of_hash ){     print "\n" , $key, " : ", $value, ;    } } このスクリプトを実行すると Nov 4 Oct 8 という3つめのハッシュのなかみが3回出力されてしまいます。自分では3つの別のハッシュをつくっているつもりでも、どうやら1種類しか作れていない、もしくはハッシュが上書きされているようなのですが、原因がわかりません。 この例だけ見るとハッシュのリファレンスを使う必要はないのですが、実際にはもうすこし大きいスクリプトで"ハッシュのリファレンスの配列を他のサブルーチンに渡す"ということを想定しており、これが解決できず先に進めない状態です。 アドバイス、解決法がわかったら教えていただけないでしょうか。

    • ベストアンサー
    • Perl
  • 初心者です。Perlではどんな時変数宣言は必要ですか?

    Perl初心者です。 マニュアル本を見ると、Perlでは特に変数を宣言しなくても扱えるようになっていますよね。 今まで幾つか自作CGIプログラムを作ってきたのですが、一度も my や localといった変数宣言を使った事がありません。 プログラム自体は正常に稼働しているので今まで全く無視していたのですが、こちらのPerlカテゴリの質問などを参照させていただいていると、皆さん必ず変数を my で宣言してらっしゃいます。 これは行った方がよいものなのですか? 宣言する事のメリット、宣言しない事のリスク、もしくはしたほうが良い場合、しない方が良い場合等を教えてください。とりあえず、サブルーチン内で宣言すると他のサブルーチンでは使えないらしい、という事だけは朧気に… よろしくお願いします。

    • ベストアンサー
    • Perl
  • Perlのサブルーチンの引数引継ぎ?

    こんにちは。 Perlの引数について質問です。 サブルーチンに引数を渡し、サブルーチンの中で他のサブルーチンをよんでいます。 すると引数を渡していないサブルーチンにまで引数が与えられてしまっているようです。 @_で受け取ると引数が渡されてしまい、$_[0]だと渡されずにすみます。 これはなぜなのでしょうか?Perlには引数を引き継ぐルールでもあるのでしょうか? 以下、サンプルです。 -------------------------------- &test('引数です'); sub test { &test2; } sub test2 { (my $hoge) = @_; } -------------------------------- $hogeに「引数です」が入ります。 &test2;を&test2();とすると大丈夫です。

    • ベストアンサー
    • Perl