6

サブルーチンを使用して、いくつかの異なるハッシュマップを作成しています。現在、参照によってハッシュマップを渡していますが、これを複数回実行すると競合します。値でハッシュを渡すか、ハッシュ参照を渡す必要がありますか?

use strict;
use warnings;

sub fromFile($){
    local $/;
    local our %counts =();
     my $string = <$_[0]>;
    open FILE, $string or die $!;
    my $contents = <FILE>;
    close FILE or die $!;

    my $pa = qr{
        ( \pL {2} )
        (?{
            if(exists $counts{lc($^N)}){
                $counts{lc($^N)} = $counts{lc($^N)} + 1;
            }
            else{
                $counts{lc($^N)} = '1';
            }
        })
        (*FAIL)
    }x;

     $contents =~ $pa;

    return %counts;

}

sub main(){
    my %english_map = &fromFile("english.txt");
    #my %german_map = &fromFile("german.txt");
}

main();

異なるtxtファイルを個別に実行しても問題はありませんが、両方で競合が発生します。

4

3 に答える 3

14

3つのコメント:

参照の受け渡しと参照による受け渡しを混同しないでください

参照を渡すことは、参照(値のタイプ)を含むスカラーを渡すことです。

コンパイラーは、コピーを作成せずに引数を渡すときに、参照によって引数を渡します。

コンパイラーは、引数のコピーを渡すときに、引数を値で渡します。

引数は常にPerlで参照によって渡されます

関数のパラメーター(の要素@_)を変更すると、呼び出し元の対応する変数が変更されます。これが、パラメータをコピーする規則が存在する理由の1つです。

my ($x, $y) = @_;   # This copies the args.

もちろん、パラメーターをコピーする主な理由は、パラメーターに「名前を付ける」ことですが、の要素を@_直接使用することで得られる厄介な驚きから私たちを救うことができます。

$ perl -E'sub f { my ($x) = @_; "b"=~/(.)/; say $x;    } "a"=~/(.)/; f($1)'
a

$ perl -E'sub f {               "b"=~/(.)/; say $_[0]; } "a"=~/(.)/; f($1)'
b

Perlでは引数として配列やハッシュを渡すことはできません

Perlサブに渡すことができるのはスカラーのリストだけです。(これは、1つで返すことができる唯一のものでもあります。)

リストコンテキストで@a評価されるので、$a[0], $a[1], ...

foo(@a)

と同じです

foo($a[0], $a[1], ...)

そのため、サブに渡したい配列またはハッシュへの参照を作成し、その参照を渡します。

そうしないと、配列またはハッシュがスカラーのリストに評価され、サブ内で再構築する必要があります。それは高価であるだけでなく、次のような場合には不可能です

foo(@a, @b)

によって返された引数の数とfooによって返された引数の数を知る方法がないためです。@a@b

プロトタイプを使用して配列またはハッシュが引数として渡されているように見せることは可能ですが、プロトタイプは配列/ハッシュへの参照を自動的に作成するだけであり、それが実際にサブに渡されることに注意してください。

于 2013-03-10T02:29:54.470 に答える
9

いくつかの理由から、参照渡しを使用する必要がありますが、表示するコードは値ごとにハッシュを返します。

  • のような組み込み変数を除いてでmyはなく、可能な限り小さなスコープに対してのみ使用する必要があります。local$/

  • サブルーチンのプロトタイプは、ほとんど決して良い考えではありません。それらは非常に具体的なことをします、そしてあなたがそれが何であるかを知らないならば、あなたはそれらを使うべきではありません。

  • のようにアンパサンドシジルを使用してサブルーチンを呼び出すことは&fromFile("english.txt")、約20年前のPerl4以降正しくありませんでした。これは、少なくとも2つの異なる方法でサブルーチンに配信されるパラメーターに影響を与えるため、お勧めできません。

  • なぜファイルグロブを使用しているのかわかりませんmy $string = <$_[0]>。パラメータとして渡されるファイル名のワイルドカードを期待していますか?その場合、最初に一致するファイルのみを開いて読み取ることになります。それ以外の場合、globは不要です。

  • のような字句ファイルハンドルは、のような$fhベアワードファイルハンドルよりも優れFILEており、破棄されると暗黙的に閉じられます。通常は、宣言されているブロックの最後にあります。

  • ハッシュ%countsがどのように入力されるかわかりません。正規表現だけでハッシュを埋めることはできませんが、私はあなたを信頼する必要があります!

このバージョンを試してください。Perlに精通している人は、キャメルケースの変数名を使用しないことを(皮肉なことに!)感謝します。mainまた、サブルーチンが宣言されて呼び出されることはめったにありません。それはC、これはPerlです。

更新元の正規表現が行ったことを実行するようにこのコードを変更しました。

use strict;
use warnings;

sub from_file {

    my ($filename) = @_;

    my $contents = do {
        open my $fh, '<', $filename or die qq{Unable to open "$filename": $!};
        local $/;
        my $contents = <$fh>;
    };

    my %counts;
    $counts{lc $1}++ while $contents =~ /(?=(\pL{2}))/g;

    return \%counts;
}

sub main {
    my $english_map = from_file('english.txt');
    my $german_map  = from_file('german.txt');
}

main();
于 2013-03-09T23:06:44.177 に答える
4

参照を使用することも、ハッシュまたは配列全体を渡すこともできます。あなたの選択。どちらかを選択する可能性のある2つの問題があります。

  1. 他のパラメータを渡す
  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";
}

サブルーチンfoo1は、渡された配列の2番目の要素を変更していることに注意してください。ただし、に渡しても@arrayfooサブルーチンは。の値を変更しません@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

于 2013-03-10T01:37:54.563 に答える