8

私はPerlを初めて使用し、現在Perlオブジェクト指向を学習しており、コンストラクターの作成に出くわしました。サブルーチンの名前に使用newすると、最初のパラメーターがパッケージ名になるようです。

コンストラクターはキーワードを使用する必要がありnewますか? それともnew、パッケージ名を使用してサブルーチンを呼び出すときに、最初に渡されるパラメーターがパッケージ名になるためですか?

packagename->new;

サブルーチンに他の名前がある場合、最初のパラメーターはオブジェクトへの参照になりますか? それとも、サブルーチンがオブジェクトへの参照を介して呼び出されるため、渡される最初のパラメーターがオブジェクトへの参照になるためですか?

$objRef->subroutine;
4

2 に答える 2

12

注: 以下のすべての例は、説明のために単純化されています。

メソッドについて

はい。それで合っています。メソッドとして呼び出された 場合new、関数の最初の引数は、呼び出した対象になります。

メソッドの呼び出しには 2 つの「フレーバー」がありますが、どちらの方法でも結果は同じです。1 つのフレーバーは、二項演算子という演算子に依存しています->。もう 1 つのフレーバーは、引数の順序付けに依存します。これは、双方向動詞が英語で機能する方法です。ほとんどの人は与格/両他動詞スタイルをビルトインでのみ使用します —おそらくコンストラクターでのみ使用しますが、それ以外ではほとんど使用しません。

ほとんどの (すべてではない) 状況では、これらの最初の 2 つは同等です。

1.メソッドの与格呼び出し

これは位置的なもので、語順を使用して何が起こっているかを判断するものです。

use Some::Package;
my $obj1 = new Some::Package NAME => "fred"; 

ここではメソッド矢印を使用していないことに注意してください->。これは、Perl 自体が独自の関数の多くで使用するものです。

 printf STDERR "%-20s: %5d\n", $name, $number;

ほぼすべての人が同等のものよりも好む:

 STDERR->printf("%-20s: %5d\n", $name, $number);

しかし、最近では、人々が物事を混乱させ続けているため、この種の与格呼び出しは、ほとんどビルトインにのみ使用されています。

2.メソッドのアロー呼び出し

矢印の呼び出しは、大部分がより明確でクリーンになり、Perl の構文解析の雑草に巻き込まれる可能性が低くなります。可能性は低いと言ったことに注意してください。私は、そこにすべての不運がなかったとは言いませんでした。しかし、この回答の目的のために、そのふりをしましょう。

use Some::Package;
my $obj2 = Some::Package->new(NAME => "fred");

実行時に、派手な奇妙さや継承の問題がなければ、実際の関数呼び出しは次のようになります。

 Some::Package::new("Some::Package", "NAME", "fred");

たとえば、Perl デバッガーでスタック ダンプを実行した場合、呼び出しチェーンに前の行のようなものが含まれます。

メソッドを呼び出すと常にパラメーター リストの前に invocant が付くため、メソッドとして呼び出されるすべての関数は、その「余分な」最初の引数を考慮する必要があります。これは非常に簡単に実行できます:

package Some::Package;
sub new {
   my($classname, @arguments) = @_;
   my $obj = { @arguments };
   bless $obj, $classname;
   return $obj;
}

これは、コンストラクターを呼び出す新しい最も頻繁な方法と、内部で何が起こるかを非常に単純化した例にすぎません。実際の製品コードでは、コンストラクターはもっと慎重になります。

メソッドと間接化

コンパイル時にクラス名またはメソッド名がわからない場合があるため、変数を使用して、どちらか一方、または両方を保持する必要があります。プログラミングにおける間接化は、自然言語における間接オブジェクトとは異なるものです。インダイレクションとは、何か他のものを含む変数があることを意味するだけなので、変数を使用してその内容を取得します。

print 3.14;    # print a number directly

$var = 3.14;   # or indirectly
print $var;

変数を使用して、メソッドの引数だけでなく、メソッド呼び出しに関連する他のものを保持できます。

3. 間接メソッド名を使用したアロー呼び出し:

メソッド名がわからない場合は、その名前を変数に入れることができます。与格の呼び出しではなく、矢印の呼び出しでのみこれを試してください。

use Some::Package;
my $action = (rand(2) < 1) ? "new" : "old";
my $obj    = Some::Package->$action(NAME => "fido");

ここでは、メソッド名自体は実行時まで不明です。

4. 間接クラス名を使用したアロー呼び出し:

ここでは、変数を使用して、使用するクラスの名前を含めます。

my $class = (rand(2) < 1) 
              ? "Fancy::Class" 
              : "Simple::Class";
my $obj3 = $class->new(NAME => "fred");

ここで、いずれかのクラスをランダムに選択します。

実際には、この方法でも与格呼び出しを使用できます。

my $obj3 = new $class NAME => "fred";

しかし、それは通常、ユーザー メソッドでは行われません。ただし、ビルトインで発生することもあります。

my $fh = ($error_count == 0) ? *STDOUT : *STDERR;
printf $fh "Error count: %d.\n", $error_count;

これは、与格スロットで式を使用しようとしても、一般にブロックがないと機能しないためです。それ以外の場合は、配列またはハッシュからの単一の要素ではなく、単純なスカラー変数にすることしかできません。

printf { ($error_count == 0) ? *STDOUT : *STDERR } "Error count: %d.\n", $error_count;

またはもっと簡単に:

print { $fh{$filename} } "Some data.\n";

これはかなり醜いです。

呼び出し元に注意してもらいます

これは完全には機能しないことに注意してください。与格オブジェクト スロットのリテラルは、そこでの変数の動作とは異なります。たとえば、リテラルファイルハンドルの場合:

print STDERR;

意味

print STDERR $_;

ただし、次のように間接ファイルハンドルを使用する場合:

print $fh;

それは実際に意味します

print STDOUT $fh;

これはおそらくあなたが望んでいたことを意味する可能性は低いです:

print $fh $_;

別名

$fh->print($_);

高度な使用方法:二重性質の方法

メソッド呼び出しの矢印->に関する問題は、左側のオペランドがクラス名を表す文字列であるか、オブジェクト インスタンスを表す祝福された参照であるかにとらわれないことです。

もちろん、$classパッケージ名を含むことを正式に要求するものはありません。いずれかである可能性があり、そうである場合、正しいことを行うのはメソッド自体次第です。

use Some::Class;

my $class = "Some::Class";
my $obj   = $class->new(NAME => "Orlando"); 

my $invocant = (rand(2) < 1) ? $class : $obj;
$invocant->any_debug(1);

これには、any_debug呼び出し元が祝福されているかどうかに応じて異なる処理を行う、かなり手の込んだメソッドが必要です。

package Some::Class;
use Scalar::Util qw(blessed);

sub new {
   my($classname, @arguments) = @_; 
   my $obj = { @arguments };
   bless $obj, $classname;
   return $obj;
}   

sub any_debug {
    my($invocant, $value) = @_;
    if (blessed($invocant)) {
        $invocant->obj_debug($value);
    } else {
        $invocant->class_debug($value);
    }
}

sub obj_debug {
    my($self, $value) = @_;
    $self->{DEBUG} = $value;
}

my $Global_Debug;
sub class_debug {
    my($classname, $value) = @_;
    $Global_Debug = $value;
}

ただし、これはかなり高度で巧妙な手法であり、まれな状況にしか適用できません。適切に処理しないと混乱を招く可能性があるため、ほとんどの状況ではお勧めできません。

于 2014-06-08T18:19:44.640 に答える