クラスの「new」メソッド内でPerlの「bless」キーワードを使用していることを理解しています。
sub new {
my $self = bless { };
return $self;
}
しかし、そのハッシュ参照に対して「bless」とは正確には何をしているのでしょうか?
一般にbless
、オブジェクトをクラスに関連付けます。
package MyClass;
my $object = { };
bless $object, "MyClass";
これで、でメソッドを呼び出すと$object
、Perlはメソッドを検索するパッケージを認識します。
例のように2番目の引数を省略すると、現在のパッケージ/クラスが使用されます。
わかりやすくするために、例は次のように記述されている場合があります。
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
編集:もう少し詳細については、kixxの良い答えを参照してください。
bless
参照をパッケージに関連付けます。
参照先が何であっても構いません。ハッシュ (最も一般的なケース)、配列 (あまり一般的ではありません)、スカラー (通常、これは裏返しのオブジェクトを示します)、正規表現のいずれかです。 、サブルーチン、または TYPEGLOB (役に立つ例については、Damian Conway 著の『Object Oriented Perl: A Comprehensive Guide to Concepts and Programming Techniques』という本を参照してください)、またはファイルまたはディレクトリ ハンドルへの参照 (最も一般的でないケース) ですらあります。
-ingの効果bless
は、祝福された参照に特別な構文を適用できることです。
たとえば、bless された参照が(パッケージ "Class" に$obj
関連付けられている) に格納されている場合、サブルーチンが呼び出され、最初の引数として参照が渡され、その後に残りの引数が続きます ( )。サブルーチンは、パッケージ「クラス」で定義する必要があります。パッケージ「Class」にサブルーチンがない場合、他のパッケージのリスト (パッケージ「Class」の配列から取得) が検索され、最初に見つかったサブルーチンが呼び出されます。bless
$obj->foo(@args)
foo
$obj
@args
foo
@ISA
foo
短いバージョン:そのハッシュを現在のパッケージ名前空間にアタッチされているものとしてマークします(そのパッケージがそのクラス実装を提供するように)。
この関数は、REF によって参照されるエンティティに、それが現在 CLASSNAME パッケージ内のオブジェクトであること、または CLASSNAME が省略されている場合は現在のパッケージであることを通知します。bless の 2 引数形式の使用をお勧めします。
例:
bless REF, CLASSNAME
bless REF
戻り値
この関数は、CLASSNAME に bless されたオブジェクトへの参照を返します。
例:
以下は、その基本的な使用法を示すコード例です。オブジェクト参照は、パッケージのクラスへの参照を祝福することによって作成されます-</p>
#!/usr/bin/perl
package Person;
sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_ssn => shift,
};
# Print all the values just for clarification.
print "First Name is $self->{_firstName}\n";
print "Last Name is $self->{_lastName}\n";
print "SSN is $self->{_ssn}\n";
bless $self, $class;
return $self;
}
ここにあるものは私にはあまりクリックされなかったので、ここで答えを提供します.
Perl の bless 関数は、パッケージ内のすべての関数への参照を関連付けます。
なぜこれが必要なのですか?
JavaScript で例を表現することから始めましょう。
(() => {
'use strict';
class Animal {
constructor(args) {
this.name = args.name;
this.sound = args.sound;
}
}
/* [WRONG] (global scope corruption)
* var animal = Animal({
* 'name': 'Jeff',
* 'sound': 'bark'
* });
* console.log(animal.name + ', ' + animal.sound); // seems good
* console.log(window.name); // my window's name is Jeff?
*/
// new is important!
var animal = new Animal(
'name': 'Jeff',
'sound': 'bark'
);
console.log(animal.name + ', ' + animal.sound); // still fine.
console.log(window.name); // undefined
})();
クラス構成を取り除いて、それなしでやり遂げましょう。
(() => {
'use strict';
var Animal = function(args) {
this.name = args.name;
this.sound = args.sound;
return this; // implicit context hashmap
};
// the "new" causes the Animal to be unbound from global context, and
// rebinds it to an empty hash map before being constructed. The state is
// now bound to animal, not the global scope.
var animal = new Animal({
'name': 'Jeff',
'sound': 'bark'
});
console.log(animal.sound);
})();
この関数は、順序付けされていないプロパティのハッシュ テーブルを取り (2016 年の動的言語では特定の順序でプロパティを記述する必要がないため)、それらのプロパティを含むハッシュ テーブルを返します。全体のグローバル コンテキストを返します (例: ブラウザーのウィンドウまたは nodejs のグローバル)。
Perl には「this」も「new」も「class」もありませんが、同様に動作する関数を持つことができます。コンストラクターもプロトタイプもありませんが、新しい動物を自由に作成し、個々のプロパティを変更できます。
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
ここで問題があります。動物の声を印刷する代わりに、動物に自分で音を出してもらいたい場合はどうでしょうか。つまり、動物自身の音を出力する関数 performSound が必要です。
これを行う 1 つの方法は、個々の動物に音の出し方を教えることです。これは、各 Cat が performSound に対して独自の複製関数を持っていることを意味します。
# self contained scope
(sub {
my $Animal = (sub {
$name = $_[0]{'name'};
$sound = $_[0]{'sound'};
return {
'name' => $name,
'sound' => $sound,
'performSound' => sub {
print $sound . "\n";
}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
$animal->{'performSound'}();
})->();
動物が構築されるたびに performSound がまったく新しい関数オブジェクトとして配置されるため、これは悪いことです。10000匹の動物は10000回のperformSoundsを意味します。自分の音を検索して出力するすべての動物で使用される単一の関数 performSound が必要です。
(() => {
'use strict';
/* a function that creates an Animal constructor which can be used to create animals */
var Animal = (() => {
/* function is important, as fat arrow does not have "this" and will not be bound to Animal. */
var InnerAnimal = function(args) {
this.name = args.name;
this.sound = args.sound;
};
/* defined once and all animals use the same single function call */
InnerAnimal.prototype.performSound = function() {
console.log(this.name);
};
return InnerAnimal;
})();
/* we're gonna create an animal with arguments in different order
because we want to be edgy. */
var animal = new Animal({
'sound': 'bark',
'name': 'Jeff'
});
animal.performSound(); // Jeff
})();
ここで、Perl との類似点がちょっと止まります。
JavaScript の new 演算子はオプションではありません。これがないと、オブジェクト メソッド内の "this" によってグローバル スコープが破損します。
(() => {
// 'use strict'; // uncommenting this prevents corruption and raises an error instead.
var Person = function() {
this.name = "Sam";
};
// var wrong = Person(); // oops! we have overwritten window.name or global.main.
// console.log(window.name); // my window's name is Sam?
var correct = new Person; // person's name is actually stored in the person now.
})();
構築時にハードコーディングするのではなく、その動物自身の音を検索する各動物に対して 1 つの関数が必要です。
Blessing を使用すると、パッケージをオブジェクトのプロトタイプとして使用できます。このようにして、オブジェクトは「参照先」である「パッケージ」を認識し、その「パッケージ オブジェクト」のコンストラクターから作成された特定のインスタンスに「到達」するパッケージ内の関数を持つことができます。
package Animal;
sub new {
my $packageRef = $_[0];
my $name = $_[1]->{'name'};
my $sound = $_[1]->{'sound'};
my $this = {
'name' => $name,
'sound' => $sound
};
bless($this, $packageRef);
return $this;
}
# all animals use the same performSound to look up their sound.
sub performSound {
my $this = shift;
my $sound = $this->{'sound'};
print $sound . "\n";
}
package main;
my $animal = Animal->new({
'name' => 'Cat',
'sound' => 'meow'
});
$animal->performSound();
要約/TL;DR :
Perl には、「this」、「class」、または「new」はありません。オブジェクトをパッケージにblelessすると、そのオブジェクトにパッケージへの参照が与えられ、パッケージ内の関数を呼び出すと、それらの引数は1スロット分オフセットされ、最初の引数($_[0]またはシフト)は次と同等になりますJavaScriptの「これ」。次に、JavaScript のプロトタイプ モデルをいくらかシミュレートできます。
残念ながら、実行時に「新しいクラス」を作成することは(私の理解では)不可能です。各「クラス」に独自のパッケージが必要なためです。一方、javascript では、「new」キーワードとしてパッケージはまったく必要ありません。実行時にパッケージとして使用する匿名ハッシュマップを作成し、その場で新しい関数を追加したり関数を削除したりできます。
Moose など、表現力におけるこの制限を橋渡しする独自の方法を作成する Perl ライブラリがいくつかあります。
なぜ混乱するのですか?:
パッケージのせい。私たちの直感は、オブジェクトをそのプロトタイプを含むハッシュマップにバインドするように指示します。これにより、JavaScript のように実行時に「パッケージ」を作成できます。Perl にはそのような柔軟性がなく (少なくとも組み込みではありません。それを発明するか、他のモジュールから取得する必要があります)、その結果、実行時の表現力が妨げられます。それを「祝福」と呼んでも、どちらにもあまりメリットはありません。
やりたいこと:
このようなものですが、プロトタイプマップへのバインディングが再帰的であり、明示的に行う必要はなく、暗黙的にプロトタイプにバインドされます。
ここに単純な試みがあります: 問題は、「呼び出し」が「それを呼び出したもの」を知らないことです。そのため、オブジェクトがメソッドを持っているかどうかをチェックするユニバーサル perl 関数「objectInvokeMethod(object, method)」である可能性があります。 、またはそのプロトタイプがそれを持っているか、そのプロトタイプがそれを持っているか、最後に到達してそれを見つけるかどうかにかかわらず (プロトタイプの継承)。Perl にはそれを行うための優れた eval マジックがありますが、それは後で試してみるために残しておきます。
とにかくここにアイデアがあります:
(sub {
my $Animal = (sub {
my $AnimalPrototype = {
'performSound' => sub {
return $_[0]->{'sound'};
}
};
my $call = sub {
my $this = $_[0];
my $proc = $_[1];
if (exists $this->{$proc}) {
return $this->{$proc}->();
} else {
return $this->{prototype}->{$proc}->($this, $proc);
}
};
return sub {
my $name = $_[0]->{name};
my $sound = $_[0]->{sound};
my $this = {
'this' => $this,
'name' => $name,
'sound' => $sound,
'prototype' => $AnimalPrototype,
'call' => $call
};
};
})->();
my $animal = $Animal->({
'name' => 'Jeff',
'sound'=> 'bark'
});
print($animal->{call}($animal, 'performSound'));
})->();
とにかく、うまくいけば、誰かがこの投稿を役に立つと思うでしょう.
私はこの考えに従って、オブジェクト指向 Perl の開発を導きました。
すべてのデータ構造参照をクラスに関連付けてください。Perl が (一種のツリーで) 継承構造を作成する方法を考えると、オブジェクト モデルを利用して合成用のオブジェクトを作成するのは簡単です。
オブジェクトと呼ばれるこの関連付けについて、オブジェクトの内部状態とクラスの動作が分離されていることを常に念頭に置いて開発します。また、任意のパッケージ/クラスの動作を使用するために、任意のデータ参照を許可/許可することができます。パッケージは、オブジェクトの「感情」の状態を理解できるためです。
たとえば、任意の Bug オブジェクトが bless されたハッシュになると確信できる場合は、(ついに!) Bug::print_me メソッドで不足しているコードを埋めることができます。
package Bug;
sub print_me
{
my ($self) = @_;
print "ID: $self->{id}\n";
print "$self->{descr}\n";
print "(Note: problem is fatal)\n" if $self->{type} eq "fatal";
}
これで、Bug クラスに bless された任意のハッシュへの参照を介して print_me メソッドが呼び出されるたびに、$self 変数が最初の引数として渡された参照を抽出し、print ステートメントが bless されたハッシュのさまざまなエントリにアクセスします。