- ベストアンサー
CからPerlへの変換がうまくいきません
Cで作った二項分布の計算プログラムをPerlで書き直しています。 ユーザ関数の使い方がよくわかりません。 どこが間違っているか教えていただけませんでしょうか? 余談ですが、ソースが左揃えになるのを防ぎたいのですが…。 #!/usr/bin/perl $p = 0.5, $s = 0, $t = 0, $combination = 0, $binarydistribution = 0; printf("n="); $n = <STDIN>; for ($r = 0; $r <= $n; $r++){ $combination = &factorial($n) / (&factorial($r) * &factorial($n - $r)); $s = 1; for ($i = 1; $i <= $r; $i++){ $s = $s * $p; } $t = 1; for ($i = 1; $i <= $n - $r; $i++){ $t *= (1 - $p); } $binarydistribution = $combination * $s * $t; printf("%.15f\n", $binarydistribution); } sub factorial{ $j = @_; $x = 1; for ($i = 1; $i <= $j; $i++){ $x *= $i; } return $x; }
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
Perl の場合、サブルーチンの引数の受け渡しを、配列 @_ を使用して行います。 $j = @_; の箇所を ( $j ) = @_; 括弧でくくってあげれば良いでしょう。 # あと、できれば my をつけたほうが良いです。 # # my ( $j ) = @_; # # こうすることで、変数をサブルーチン内に局所化できます。 # いわゆるローカル変数というやつですね。 そうでなければ、No1 BLUEPIXYさんがご回答されているように my $j = shift; とします。 これは、以下と同様の意味で、引数リスト@_ から先頭要素を取り出すことを意味します。 $j = shift( @_ ); どうしてこのような面倒な話になるのかと言うと、Perl が、状況によって勝手にコンテキストを変換してしまうためです。 簡単に言えば、配列をスカラー変数に代入すると、その要素数が取り出せるんです。 この場合も $j = @_; としてしまった場合、$j (スカラー変数) には @_ (配列) の要素数 (この場合は 1 ですよね?) が代入されることになります。 括弧でスカラー変数をくくれば、それは配列として扱われます。 ( $j ) = @_; とすれば、( $j ) は要素 $j をもつ配列を意味しますので、この式で配列代入が行われることになり、$j には $_[0] が代入されます。 ちなみに、2つ以上の引数があった場合にも同様にします。 引数を2個とるサブルーチンの場合は、以下のようにします。 -------------------------------------------------------- &subrtn1( $h, $p ); …… sub subrtn1 { # 引数の受け渡し my( $hoge, $piyo ) = @_; } -------------------------------------------------------- $hoge には $_[0]、$piyo には $_[1] がそれぞれ代入されることになります。 なお、shift を使う場合は、以下のようにします。 -------------------------------------------------------- my $hoge = shift; my $piyo = shift; -------------------------------------------------------- こんな感じでいかがでしょうか。
その他の回答 (2)
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
>なぜそれで直ったのか理由が知りたいです。 ほとんど#2の方が丁寧に説明されている通りです。 サブルーチンへの引数は @_ という配列に自動的に割り当てられます。 $j = @_; のようにスカラー変数に配列を代入するという式は配列のサイズを代入することになります。 なので、 $j は いつも1になるというのが直接的な動作不良の原因です。 shift は、配列から先頭を切り出す関数で 切り出された内容は配列からなくなり以降の要素が順繰りに上がるような感じになります、それでシフトというのです。 shift は引数としての配列が省略されたとき @_ が引数に指定されたとして処理します。 shiftを使わずに $j=@_[0]; としてもいいですね。 後、蛇足ですが、 sub factorial($){… の様に指定すると引数の数が(スカラーで)1つであることをコンパイル時にチェックしてくれます。 my 演算子を使うと変数を局所変数にしてくれます。 Perlでは、Cでのようにサブルーチンでの変数を 自動的には局所変数にはしてくれません。 例えば $i など、ループの変数としてよく使いますが、 そういうメインのループから関数を呼び出した時など致命的になりがちです。 (そういえばsubで $i 使ってますね my $i; しておきましょう)
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
とりあえず my $j = shift; my $x = 1; としてみたらどうでしょうか
お礼
ありがとうございます。解決しました。 $jにつけるmyは今回は特に必要ありませんでした。 実務におけるシステム開発では、変数のスコープを必要な範囲で狭くしたほうがよいようですので、参考になりました。 shiftというのは今回初めて知りました。 勉強になりました。
補足
質問者です。 サブルーチンの中をおっしゃるとおりに変えてみたらうまくいきました。 なぜそれで直ったのか理由が知りたいです。 私の持っている本にはユーザ関数について書いてないので、Webを頼りに組んでみたのですが、参照したサイトはmyやshiftは使っていませんでした。 @_がどのような役割をしているのかもよくわかりません。 myやshiftがあるのとないのでは何が違うのでしょうか? 参考にしたサイト http://www.kent-web.com/perl/chap8.html http://flex.ee.uec.ac.jp/texi/perl/perl_53.html
お礼
解決した上にさらに勉強になりました。 そうなんです。コンテキストを変換してしまうので、$jに要素数が入っているのでおかしいと思っていたんです。 ()をつければ0番目の要素が入るんですね。これはとても役立つ知識だと思います。 shiftで先頭を切ってまた()で代入することによって、順番に要素を取り出すこともできるのが便利です。 my( $hoge, $piyo ) = @_;とすることで、一度に複数の要素を別々の変数に入れることもできるんですね。 Perlはある動作をさせるのに表現方法が複数あるので覚えるのが大変です。 どれか一つ得意な構文のパターンを身につけると良さそうですね。 ありがとうございました。