参照を使用することも、ハッシュまたは配列全体を渡すこともできます。あなたの選択。どちらかを選択する可能性のある2つの問題があります。
- 他のパラメータを渡す
- メモリ管理
Perlには実際にはサブルーチンパラメータがありません。代わりに、パラメータの配列を渡すだけです。サブルーチンを使用している場合は、どの配列にさらに多くの要素があるかを確認します。私はこれを行うことができませんでした:
foo(@first, @second);
なぜなら、私が渡すのは、両方のすべてのメンバーを組み合わせた1つの大きな配列だけだからです。これはハッシュにも当てはまります。2つのハッシュを取り、共通のキーを持つものを見つけるプログラムを想像してみてください。
@common_keys = common(%hash1, %hash1);
繰り返しますが、両方のハッシュのすべてのキーとその値を1つの大きな配列に結合しています。
この問題を回避する唯一の方法は、参照を渡すことです。
foo(\@first, \@second);
@common_keys = common(\%hash1, \%hash2);
この場合、これら2つのハッシュがメモリに格納されているメモリ位置を渡します。私のサブルーチンはそれらのハッシュ参照を使用できます。ただし、2番目の説明で説明するように注意する必要があります。
参照を渡す2番目の理由は、メモリ管理です。私の配列またはハッシュが数十のエントリである場合、それは実際にはそれほど重要ではありません。ただし、ハッシュまたは配列に10,000,000のエントリがあると想像してください。これらすべてのメンバーをコピーするには、かなりの時間がかかる可能性があります。参照を渡すことでメモリを節約できますが、コストがかかります。ほとんどの場合、メインプログラムに影響を与えない方法としてサブルーチンを使用しています。これが、サブルーチンが独自の変数を使用することを想定している理由であり、ほとんどのプログラミングコースで変数スコープについて教えられている理由です。
しかし、私が参照を渡すとき、私はその範囲を破っています。これは、参照を渡さない単純なプログラムです。
#! /usr/bin/env perl
use strict;
use warnings;
my @array = qw(this that the other);
foo (@array);
print join ( ":", @array ) . "\n";
sub foo {
my @foo_array = @_;
$foo_array[1] = "FOO";
}
サブルーチンfoo
1は、渡された配列の2番目の要素を変更していることに注意してください。ただし、に渡しても@array
、foo
サブルーチンは。の値を変更しません@array
。これは、サブルーチンが(によって作成されたmy @foo_array = @_;
)コピーを処理しているためです。サブルーチンが存在すると、コピーは消えます。
このプログラムを実行すると、次のようになります。
this:that:the:other
さて、これは同じプログラムですが、参照を渡すことを除いて、メモリ管理のために、その参照を使用します。
#! /usr/bin/env perl
use strict;
use warnings;
my @array = qw(this that the other);
foo (\@array);
print join ( ":", @array ) . "\n";
sub foo {
my $foo_array_ref = shift;
$foo_array_ref->[1] = "FOO";
}
このプログラムを実行すると、次のようになります。
this:FOO:the:other
これは、配列を渡さず、その配列への参照を渡すためです。を保持するのと同じメモリ位置です@array
。したがって、サブルーチンで参照を変更すると、メインプログラムで参照が変更されます。ほとんどの場合、これは行いたくありません。
これを回避するには、参照を渡し、その参照を配列にコピーします。たとえば、私がこれを行った場合:
sub foo {
my @foo_array = @{ shift() };
別の配列への参照のコピーを作成します。変数は保護されますが、時間とメモリを必要とする別のオブジェクトに配列をコピーしていることを意味します。私が最初にプログラミングをしていた1980年代に、これは大きな問題でした。ただし、ギガバイトメモリとクアッドコアプロセッサのこの時代では、主な問題はメモリ管理ではなく、保守性です。配列またはハッシュに1,000万のエントリが含まれている場合でも、時間やメモリの問題に気付くことはおそらくないでしょう。
これも逆に機能します。サブルーチンからハッシュまたはハッシュ全体への参照を返すことができます。多くの人は参照を返すのが好きですが、これは問題になる可能性があります。
オブジェクト指向のPerlプログラミングでは、参照を使用してオブジェクトを追跡します。通常、他の値、配列、ハッシュを格納するために使用できるハッシュへの参照があります。
最近のプログラムでは、IDとそれらがログファイルで参照されている回数を数えていました。これはオブジェクト(ハッシュへの単なる参照)に格納されました。IDのハッシュ全体とそのカウントを返すメソッドがありました。私はこれを行うことができた:
return $self->{COUNT_HASH};
しかし、ユーザーが私が渡したその参照を変更し始めた場合、どうなりましたか?IDを加算および減算するメソッドを使用せずに、実際にオブジェクトを操作していることになります。私が彼らにしてもらいたいことではありません。代わりに、新しいハッシュを作成してから、そのハッシュへの参照を返します。
my %hash_counts = % { $self-{COUNT_HASH} };
return \%hash_count;
これにより、参照が配列にコピーされ、次にその参照が配列に渡されました。これにより、データが外部からの操作から保護されます。それでも参照を返すことはできますが、ユーザーはメソッドを経由せずにオブジェクトにアクセスできなくなります。
ちなみに、私はwantarray
これを使用して、発信者がデータをどのように取得するかを選択できるようにするのが好きです。
my %hash_counts = %{ $self->{COUNT_HASH} };
return want array ? %hash_counts : \%hash_counts;
これにより、ユーザーがオブジェクトをどのように呼び出したかに応じて、参照またはハッシュを返すことができます。
my %hash_counts = $object->totals(); # Returns a hash
my $hash_counts_ref = $object->totals(); # Returns a reference to a hash
1脚注:@_
配列は、呼び出し元のサブルーチンのパラメーターと同じメモリ位置を指しています。したがって、パスしfoo(@array)
てから実行した場合$_[1] = "foo";
、の2番目の要素を変更することになります@array
。