Perlのサブルーチンでの変数宣言の違いとは?

このQ&Aのポイント
  • Perlのサブルーチンでの変数宣言方法には内外での宣言の違いがあります。
  • サブルーチンの外で変数にmyを宣言することで、その変数はサブルーチン内でも参照することができます。
  • 一方、サブルーチン内で変数にmyを宣言すると、その変数はそのサブルーチン内でのみ有効となります。
回答を見る
  • ベストアンサー

Perlのmyのサブルーチンの内外での宣言の違い

Perlでmyを使う場合、サブルーチンの内外での宣言の違いについて知りたいです。 私はよくある変数をそのプログラム内全体で使う時(カウントするだけの$iみたいなものや、DBのクエリなど)は、同じ変数名をサブルーチン内で毎回宣言するのが嫌(個人的に同じことを重複するプログラムが好きではないという主義)なので、サブルーチンの外でmyを宣言するのですが、myとかはサブルーチン外で宣言するとどんな問題が生じる可能性があるのでしょうか? また毎回サブルーチン内で宣言するのと外部で宣言するのではどういった時に問題になるのでしょうか?もちろんサブルーチンの外でmy宣言した場合はサブルーチンにその変数が引き継がれることは知っています。 リファレンス部分で変わるような記述を見たことはあります。 さしあたって問題を感じでいないのです、ふと疑問に思ったのですが、どうもそれに書かれたソースが少ないので御存知の方がいればぜひ御教授願いたいです。 <例> my $hoge; sub hoge1 { $hoge = 1; } sub hoge2 { $hoge = 5; } と sub hoge1 { my $hoge = 1; } sub hoge2 { my $hoge = 5; }

  • Perl
  • 回答数3
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

> カウントするだけの$iみたいなものや、DBのクエリなど こういうのこそ、myでスコープ限定するべきものだと思うのですが。 例えば my $i ; for( $i = 0 ; $i < 20 ; $i ++ ) { printf "before %4d:" , $i ; printf "sum %4d:" ,&hoge($i) ; printf "after %4d\n" , $i ; } sub hoge { my $max = shift ; my $ret=0; for( $i = 1 ; $i <= $max ; $i ++ ) { $ret += $i ; } return $ret; } こんなプログラム、実行したらどうなると思います? # ちなみに、 my $max=..を削除して、$i<=$max を $i <= $_[0] にすると.... $iみたいな汎用的に使うループ変数は、逆に他に影響を与えてないか注意しないといけないものです。ですから、むしろ積極的にmy で範囲を限定するのが間違いが少ないです。 これくらいの規模ならすぐにわかると思いますが、規模が大きくなると、どれが使ってない変数だかわからなくなります。 独学とのことなので、今までは、Perlの文法やコマンド、関数等を学んできたのだと思います。 しかし、そういう「言語入門」的な書籍などでは、あまり「プログラミングスタイル」といったものは載っていないと思います。 ここらで、プログラミングスタイルについての勉強もしてみませんか? とりあえずは「構造化プログラミング」をキーワードに本やサイトを探してみてください。 計算機工学の分野では古典の部類に入る考え方ですが、現在主流のオブジェクト指向等にもつながる基本的な考え方となっています。 > 同じことを重複するプログラムが好きではないという主義 これ自体はいい考えだと思いますよ。 ただ、それと、myを外に出す、というのはちょっと違うと思います。

keeeeeeeen
質問者

お礼

回答ありがとうございます。 なるほど、構造化プログラミングというんですね。 さっそく勉強したいと思います。 forはあまり使いません(foreachやwhilleの方がメモリを食わないという記述をみたので…)その際に sub hoge { $i = 0 ; foreach my $key( @hoge ){ $i++; } } みたいな書き方を良くします。 とりあえず$i = 0 ;さえ書く習慣があれば変数自体は常に0から始まりますし…。。。 ただやはり個人的ななれなので他人から見るときっとわかりにくいソースになっているんでしょうね。 そういう意味ではやはり書き方を改めるべきですね。

その他の回答 (2)

  • Gotthold
  • ベストアンサー率47% (396/832)
回答No.3

> 個人的に同じことを重複するプログラムが好きではないという主義 同じことを繰り返し書かないというのは良いスタイルですが、 今回の場合はやってることは同じではありません。 > sub hoge1 > { > my $hoge = 1; > } > sub hoge2 > { > my $hoge = 5; > } 前者は「sub hoge1の$hogeを宣言」、後者は、「sub hoge2の$hogeを宣言」なので違うことをやっています。 これらは名前が同じだけで別の変数なので、外に出すなら > my $hoge1_hoge; > my $hoge2_hoge; > sub hoge1 > { > $hoge1_hoge = 1; > } > sub hoge2 > { > $hoge2_hoge = 5; > } とでもしなければ別の変数になってくれません。 しかし、これは面倒ですし、何より無関係のサブルーチンから変数を操作される恐れがあります。 (間違って変数を操作した場合にどうなるかはNo.2の方が例示してくれてるので割愛。) 要するに、最初のようにサブルーチン内で宣言した方が楽ができるのです。 プログラミングするときは、できるだけ楽になるようにしましょ。

keeeeeeeen
質問者

お礼

回答ありがとうございます。 すいません、hogeという例がよろしくないようですね。 こちらの説明不足でうまく伝わっていないようで…。 ソースを書くのが面倒なので割愛しますが、あくまでhogeはサブルーチン内で一時的に使う関数なのでおっしゃるような自称は起きないかとは思います。 できるだけ楽に書くというのはわかります。

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

ちょっとした本 (例: らくだ本) には書いてあると思うんだけどなぁ.... my があると, その時点で「新しい変数」ができると思ってください. その有効範囲はもちろん当該場所に対する静的スコープです. また, 「変数名」もそのスコープでのみ有効です. 上の例ではサブルーチンの外で変数 $hoge を作り, その変数の名前と値をサブルーチンで共用します. だから, hoge1, hoge2 のどちらかで行った変更はもう一方でも (そして同じスコープにあるほかのどの部分からも) 見ることができます. 一方, 下の例では 2つのサブルーチンがそれぞれ個別に変数 $hoge を作ります. したがって hoge1 で宣言した $hoge は hoge2 では使えませんし, 逆に hoge2 で宣言した $hoge を hoge1 で使うこともできません (もちろん hoge1, hoge2 以外ではどちらも使えない). さらにいうと, あるスコープ my で変数を宣言した場合, その外で同名の変数を宣言していたとしてもそれを使うことは (パッケージで修飾したりしない限り) 使えません. 下の例で, hoge1 の前に my $hoge='foo'; とでも入れて, いろんなところで print してみるとその効果が分かると思います. ちなみにですが, 「my で宣言すると変数の有効範囲がスコープに制限される」ことと「サブルーチン名はスコープに制限されない」ことを組み合わせると, 「複数のサブルーチンで共用したいけど外からは使ってほしくない」変数を作ることもできます. つまり, { my $hoge = undef; sub hoge1 { $hoge = 1; } sub hoge2 { $hoge = 5; } } とすると, この $hoge はサブルーチン hoge1, hoge2 以外では使えなくなります. ただ, ポリシーを否定する気はないのですが老婆心ながら言わせてもらうと, 「同じ変数名をサブルーチン内で毎回宣言するのが嫌なので、サブルーチンの外でmyを宣言する」というのはあまりよいプログラミングスタイルではないですよ.

keeeeeeeen
質問者

お礼

詳しい説明ありがとうございます。 ポリシーを否定する気ないと言いつつも否定してますね(汗) ただ、どんなプログラミングがいいのかは独学なのでわからないもので、それが正しい書き方なのであれば各スコープで宣言するような形に変えていきたいと思います。 $hoge='foo'のくだりなどは概ね理解はしております。 ありがとうございます。

関連するQ&A

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

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

    • ベストアンサー
    • 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
  • Perlの変数宣言について

    PHPを毎日書いている者ですが、たまたまPerlのメールフォームをカスタマイズ しなければならなくなりました。 use strict宣言がある場合、変数はmyまたはlocalで宣言しなければいけないようですが、 サブルーチン内に sub form { ・・・ my ($form_value,$error_list) = @_; ・・・ print $error_list{'inquiry_type1'} としたところ、 Global symbol "%error_list" requires explicit package name at ・・・ と、宣言が無い場合に出るというエラーが出ました。 $error_listには、inquiry_type1は入っているはずなのですが、どのような 宣言をすればよいのでしょうか? よろしくお願いします。

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

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

    • ベストアンサー
    • Perl
  • 【Perl】サブルーチンから多次元配列を戻したい

    【Perl】サブルーチンから多次元配列を戻したい サブルーチン内で作った2次元配列と1つの変数(例:@listと$i)をリターンしたいのですが、どのようにすれば良いでしょうか? ↓サブルーチン(calc) return (@list, $i); ↓メイン my $a = 12; my (@list, $i) = &calc($a); 間違っていると思いますが、現時点でのプログラムを一部書きました。 ご回答よろしくお願いします!

    • ベストアンサー
    • Perl
  • サブルーチンについて

    下記のようなソースコードを書いたところエラーが出てしまいました。 $sum = $sample2($a); の箇所でエラーが起こっているようなのですが、解決の仕方が分かりません。 &sample1(5,'&sample2'); sub sample1 { ($a,$sample2) = @_; $sum = $sample2($a); print $sum; } sub sample2 { ($a) = @_; $b = 10; $sum = $a + $b; return $sum; } このように、サブルーチンの中に更にサブルーチンを書いて、なおかつそのサブルーチン名を一旦変数に格納して使用する場合はどのようにプログラムを書けば良いのでしょうか?? ""で囲ったりといろいろとしてもダメでした。 もちろん、$sum = $sample2($a); ではなく $sum = sample2($a); のように書けば良いとは思うのですが、一度サブルーチン名を変数に入れてから使用する場合はどのようにすれば良いのでしょうか?? ご回答の程よろしくお願い致します。

    • ベストアンサー
    • CGI
  • サブルーチン内のサブルーチン定義について

    サブルーチン内で定義したサブルーチンで、思い通りにならない挙動で困っています。 'test'を10万回繰り返す文字列の生成を行い、その文字列長を表示する関数を funcA とします。その生成過程では、自分の関数内で宣言した再帰関数 funcB を呼び出します。 #! /usr/local/bin/perl use strict; my $time0; for(my $i=0; $i<10; $i++){   $time0 = times();   &funcA();   print((times() - $time0). "\n"); # funcAに掛かった時間 } sub funcA {   my $buffer = '';   &funcB(1);   print length($buffer) . " : "; # $buffer の文字列長      sub funcB{     my $n = shift;     $buffer .= 'test';     return if($n==100000);     funcB($n+1);   } } この結果が、 400000 :3.063 0 :0.468 0 :0.594 0 :0.766 0 :0.859 0 :1.11 0 :1.187 0 :1.141 0 :1.343 0 :1.469 となり、初回以降 $buffer の長さが0となるのも不可解ですが、funcA の実行時間が増加していくのも理解できません. これを #! /usr/local/bin/perl use strict; my $time0; my $buffer; # 注1 $buffer をファイル内大域変数として宣言 for(my $i=0; $i<10; $i++){   $time0 = times();   &funcA();   print((times() - $time0). "\n"); } sub funcA {   $buffer = ''; # 注2 レキシカル変数宣言をやめた   &funcB(1);   print length($buffer) . " : ";      sub funcB{     my $n = shift;     $buffer .= 'test';     return if($n==100000);     funcB($n+1);   } } とすると、結果は 400004 :3.188 400004 :0.234 [以降、上にほぼ同じ] と文字列長は正しいものの,初回以降のfuncA実行時間が極端に減ります. 内部ではどういうことが起こっているのでしょうか.

    • ベストアンサー
    • 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; }

  • VC++ 高速演算コードの沢山の変数の宣言方法

     【VC++ 高速演算コードの沢山の変数を、どのように宣言すべきでしょうか?】  今日は、質問させていただきます。 もしお詳しい方がいらっしゃいましたら、 どうぞよろしくお願いいたします。  Fortranのコード(歴10年程度)を、VC++2010(歴3ヶ月程度)になおそうとしております。  ボリュームのある数値解析のコード(3000行程度ございます)を、見やすくするために、 数百行ずつでサブルーチン化したい次第でございます。    例)int main(){                ←変数の宣言場所         definition()         calculation1()         calculation2()         calculation3()         conclusion()      }  しかしmainの最初(上記「変数の宣言場所」)にまとめて宣言しております変数が200個以上ございまして、これらの半分近くを各々のサブルーチンに毎回渡そうといたしますと、引数の量が膨大になってしまいますし、 またサブルーチン内で何か追記する度に引数を追加・削除せねばなりませんので、出来れば避けたい次第でございます。  そこで「全てPublicで宣言してしまおうかな?」などと考えて検索しておりましたが、「できる限りPublic変数を宣言すべきでない」といったような記事もございましたので、悩んでおります。 (Fortran環境下では、ヘッダファイル内のPublic変数を、全サブルーチンで毎回呼び出しておりましたが、それも良くないのかな、と迷っております。)  あと、上記のような宣言方法や参照方法が計算速度に影響するものなのかどうか分からないのでございますが、 平均で10日間程度まわし続けるプログラムになりますので、出来る限り「高速化」を優先したい次第でございます。  変なご質問かもしれませんので大変恐縮でございますが、 是非アドバイスいただけないでしょうか。(書物やサイトをご紹介いただけるのでも結構でございます)  何卒よろしくお願いいたします。

専門家に質問してみよう