プロが教える店舗&オフィスのセキュリティ対策術

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;
}

A 回答 (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;
--------------------------------------------------------

こんな感じでいかがでしょうか。
    • good
    • 0
この回答へのお礼

解決した上にさらに勉強になりました。

そうなんです。コンテキストを変換してしまうので、$jに要素数が入っているのでおかしいと思っていたんです。
()をつければ0番目の要素が入るんですね。これはとても役立つ知識だと思います。
shiftで先頭を切ってまた()で代入することによって、順番に要素を取り出すこともできるのが便利です。
my( $hoge, $piyo ) = @_;とすることで、一度に複数の要素を別々の変数に入れることもできるんですね。

Perlはある動作をさせるのに表現方法が複数あるので覚えるのが大変です。
どれか一つ得意な構文のパターンを身につけると良さそうですね。

ありがとうございました。

お礼日時:2005/12/13 16:20

>なぜそれで直ったのか理由が知りたいです。


ほとんど#2の方が丁寧に説明されている通りです。
サブルーチンへの引数は
@_ という配列に自動的に割り当てられます。
$j = @_;
のようにスカラー変数に配列を代入するという式は配列のサイズを代入することになります。
なので、
$j は いつも1になるというのが直接的な動作不良の原因です。
shift は、配列から先頭を切り出す関数で
切り出された内容は配列からなくなり以降の要素が順繰りに上がるような感じになります、それでシフトというのです。
shift は引数としての配列が省略されたとき @_ が引数に指定されたとして処理します。
shiftを使わずに
$j=@_[0];
としてもいいですね。

後、蛇足ですが、
sub factorial($){…
の様に指定すると引数の数が(スカラーで)1つであることをコンパイル時にチェックしてくれます。

my 演算子を使うと変数を局所変数にしてくれます。
Perlでは、Cでのようにサブルーチンでの変数を
自動的には局所変数にはしてくれません。
例えば $i など、ループの変数としてよく使いますが、
そういうメインのループから関数を呼び出した時など致命的になりがちです。
(そういえばsubで $i 使ってますね my $i; しておきましょう)
    • good
    • 0

とりあえず


my $j = shift;
my $x = 1;
としてみたらどうでしょうか

この回答への補足

質問者です。

サブルーチンの中をおっしゃるとおりに変えてみたらうまくいきました。
なぜそれで直ったのか理由が知りたいです。

私の持っている本にはユーザ関数について書いてないので、Webを頼りに組んでみたのですが、参照したサイトはmyやshiftは使っていませんでした。
@_がどのような役割をしているのかもよくわかりません。

myやshiftがあるのとないのでは何が違うのでしょうか?


参考にしたサイト
http://www.kent-web.com/perl/chap8.html
http://flex.ee.uec.ac.jp/texi/perl/perl_53.html

補足日時:2005/12/13 11:15
    • good
    • 0
この回答へのお礼

ありがとうございます。解決しました。
$jにつけるmyは今回は特に必要ありませんでした。
実務におけるシステム開発では、変数のスコープを必要な範囲で狭くしたほうがよいようですので、参考になりました。
shiftというのは今回初めて知りました。
勉強になりました。

お礼日時:2005/12/13 16:10

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!