第7回 関数というかサブルーチン

今回は、関数というかサブルーチンというかまあ、いわゆるファンクションというかー

...


要は、機能の集まりというか....


ええい! 「関数」を学びましょう。


「関数」と「サブルーチン」の違いなのですが、

  • 関数は、「引数をうけとってなにかの値を返します」
  • サブルチーンは、「ただ処理をまとめただけ」


です。

つまり、値を返すか、返さないかみたいな違いなんですが、今や色んなプログラム言語がありますので、その違いが明白な言語もあれば、明白でない言語もあります。あなたの言語はどれですか?


ということで、Perlの関数を学びましょう。Perlの関数は、

sub 数名{ }

こんな風に定義できます。いままでさんざん登場してきましたね。
これがJavaScriptだと、

function 関数名(){ }

となります。

ちがいは、ココだ!!

そう、( ) のある/なしです。

Perlのsubというのはサブルーチンから命名されているわけですね。
というと、Perlの関数は引数を受け取ることができなさそうですが、そんなことはありません。
ですが、受け取り方が他の言語とちょっと違います。

他の言語の代表例としてもう一度 JavaScriptを見てみましょう。

function hoge(i, j, k){  }

これは、Perlだと、つぎのようになります。

sub hoge { my( $i, $j, $k ) = @_; }

なんと、Perlでは、引数は、関数の中に入ってから、受け取るのです。
引数は特殊変数 @_ に収まっています。ですから、

my( $i, $j, $k ) = @_;

で、代入してやります。

特殊変数 @_ を用いて次のように、処理をすることもできますが、

use strict;

hoge('i', 'j', 'k');  # 文字列 'i'と'j'と'k'を渡す

sub hoge {
 my ($i, $j, $k) = @_;

 print "$_[0]", "\n";
 print "$_[1]", "\n";
 print "$_[2]", "\n";

}

結果:

i
j
k

これは大変危険です。 なぜなら、 @_ はエイリアスだからです。

use strict;

my ($m_i, $m_j, $m_k) = ('i', 'j', 'k');

hoge($m_i, $m_j, $m_k);

print "--- hoge()実行後---\n";
print "$m_i", "\n";
print "$m_j", "\n";
print "$m_k", "\n";

sub hoge {
 $_[0] = 1;  # @_ の 0番目ですから、@を$に変えて$_[0]
 $_[1] = 2;
 $_[2] = 3;
}

結果:

--- hoge()実行後---
1
2
3

ほら、このとおり、呼び出しもと自体の値が変わってしまいました!!!!

ちゃんと次のように、受け取った側の関数でコピーしてやれば問題ありません。

use strict;

my ($m_i, $m_j, $m_k) = ('i', 'j', 'k');

hoge($m_i, $m_j, $m_k);

print "--- hoge()実行後---\n";
print "$m_i", "\n";
print "$m_j", "\n";
print "$m_k", "\n";

sub hoge {
 my ($i, $j, $k) = @_;  # コピーする

 $i = 1;
 $j = 2;
 $k = 3;
}

結果:

--- hoge()実行後---
i
j
k

ほらこの通り、 $m_i, $m_j, $m_kは無事です(値が書き変わっていません)。

なお、戻り値を返すときは、他の言語と同じように return が使えます。ただし、これに加えて関数の中で最後に評価された値も返せます。

use strict;

my $c = hoge();
print "$c", "\n";

sub hoge{
 return 1;
}

結果:

1

もしくは、

use strict;

my $c = hoge();
print "$c", "\n";

sub hoge{
 1; # 最後に評価される値は1
}

こういうことです。


さて、今まで例に出してきたソースで、
(再掲)

use strict;

my ($m_i, $m_j, $m_k) = ('i', 'j', 'k');

hoge($m_i, $m_j, $m_k);

print "--- hoge()実行後---\n";
print "$m_i", "\n";
print "$m_j", "\n";
print "$m_k", "\n";

sub hoge {
 my ($i, $j, $k) = @_;

 $i = 1;
 $j = 2;
 $k = 3;
}

こういうのがありました。

print "$m_i", "\n";
print "$m_j", "\n";
print "$m_k", "\n";

この部分きになりませんか? おなじ処理です。
そうだこんなときこそ関数だ! とおもったら 大間違いです!!!!
なんでそんなことのためにわざわざ関数を用意する必要があるのか!!
for文で十分です。

ほら、この通り

use strict;

my ($m_i, $m_j, $m_k) = ('i', 'j', 'k');

hoge($m_i, $m_j, $m_k);

print "--- hoge()実行後---\n";
for($m_i, $m_j, $m_k){    # for に書き換えました
 print "$_", "\n";
}

sub hoge {
 my ($i, $j, $k) = @_;

 $i = 1; # エイリアスは使っていません
 $j = 2;
 $k = 3;
}

結果:

--- hoge()実行後---
i
j
k

このように、わざわざ関数をつくらなくても、forの繰り返しで処理ができる場合が非常におおいです。どちらを使うかは実践で培っていってください。



さて、話を関数の作り方(定義の仕方)に戻しましましょう。
関数の引数として、配列を渡したい場合はやっかいです。リファレンス渡しにしないとうまくいきません。
その理由は、昔に書いていまして、
ビンゴ中西のほげほげ君のプログラムスタイルを抜本的に変える方法
に載っていますので参考になさってください。


加えて、ちょっとだけサブルーチンについて昔書いたものを紹介しておきます。
ビンゴ中西のほげほげPerlのサブルーチンの書き方 ちょっとだけ紹介
ビンゴ中西のほげほげPerlの関数への引数の受け取り方



それでは、これで、関数を使えるようになったと思いますので、
より大きいプログラムを作っていってください。


それでは また〜