回答:
一般にbless
、オブジェクトをクラスに関連付けます。
package MyClass;
my $object = { };
bless $object, "MyClass";
でメソッドを呼び出すと$object
、Perlはメソッドを検索するパッケージを認識します。
例のように2番目の引数が省略されている場合、現在のパッケージ/クラスが使用されます。
わかりやすくするために、例は次のように書くことができます。
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
bless
参照をパッケージに関連付けます。
参照が何であるかは関係ありません。ハッシュ(最も一般的なケース)、配列(それほど一般的ではない)、スカラー(通常、これはオブジェクトがインサイドアウトであることを示します)、正規表現などです。 、サブルーチン、またはTYPEGLOB(有用な例については、オブジェクト指向Perl:概念とプログラミング手法の総合ガイド(Damian Conway著)を参照)、またはファイルまたはディレクトリハンドルへの参照(最も一般的ではないケース)です。
bless
-ing の効果は、祝福された参照に特別な構文を適用できることです。
たとえば、祝福された参照が$obj
(bless
パッケージ "Class"に関連付けられて)に格納されている場合、$obj->foo(@args)
サブルーチンを呼び出し、foo
最初の引数として参照を渡し$obj
、その後に残りの引数を渡します(@args
)。サブルーチンはパッケージ「クラス」で定義する必要があります。foo
パッケージ「クラス」にサブルーチンがない場合、他のパッケージのリスト(@ISA
パッケージ「クラス」の配列から取得)が検索され、最初にfoo
見つかったサブルーチンが呼び出されます。
この関数は、REFによって参照されるエンティティに、それがCLASSNAMEパッケージ内のオブジェクト、またはCLASSNAMEが省略されている場合は現在のパッケージであることを通知します。2引数形式のblessの使用をお勧めします。
例:
bless REF, CLASSNAME
bless REF
戻り値
この関数は、CLASSNAMEにblessされたオブジェクトへの参照を返します。
例:
以下は、その基本的な使用法を示すコード例です。オブジェクト参照は、パッケージのクラスへの参照をblessすることによって作成されます-
#!/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のパフォーマンスサウンドを意味します。自分の音を検索して出力するすべての動物が使用する単一の関数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の新しい演算子はオプションではなく、それがないと、オブジェクトメソッド内の「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つの関数を設定します。
祝福により、パッケージをオブジェクトのプロトタイプとして使用できます。このようにして、オブジェクトは「参照される」「パッケージ」を認識し、その「パッケージオブジェクト」のコンストラクターから作成された特定のインスタンスに「到達する」パッケージ内の関数を持つことができます。
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」はありません。オブジェクトをパッケージにblessすると、そのオブジェクトにパッケージへの参照が与えられ、パッケージ内の関数を呼び出すと、引数は1スロットだけオフセットされ、最初の引数($ _ [0]またはshift)は次のようになります。 javascriptの「this」。次に、JavaScriptのプロトタイプモデルを多少シミュレートできます。
残念ながら、実行時に "新しいクラス"を作成することは(私の理解では)不可能です。これは、各 "クラス"が独自のパッケージを持つ必要があるためです。一方、JavaScriptでは、 "新しい"キーワードとしてパッケージをまったく必要としません。実行時にパッケージとして使用するための匿名ハッシュマップを作成します。これには、新しい関数を追加したり、その場で関数を削除したりできます。
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'));
})->();
とにかく、うまくいけば、誰かがこの投稿が役に立つと思うでしょう。
my $o = bless {}, $anything;
オブジェクトを$anything
クラスに祝福します。同様に、で指定{no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
されたクラスに「somesub」という名前のメソッドを作成し$anything
ます。これはすべて実行時に可能です。ただし、「可能性」があるからといって、毎日のコードを操作するのは良い習慣ではありません。ただし、MooseやMooなどのオブジェクトオーバーレイシステムの構築には役立ちます。
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
主張を無効にします。結局のところ、私の懸念は、実行時にパッケージシステムを操作/イントロスペクトすることの直感性が大幅に低下することになると思いますが、これまでのところ、本質的にそれができないことを示すことに失敗しました。パッケージシステムは、実行時に自身を追加/削除/検査/変更するために必要なすべてのツールをサポートしているようです。
多くの良い答えとともに、bless
-ed参照を明確に区別するのはSV
、追加のFLAGS
(OBJECT
)とSTASH
perl -MDevel::Peek -wE'
package Pack { sub func { return { a=>1 } } };
package Class { sub new { return bless { A=>10 } } };
$vp = Pack::func(); print Dump $vp; say"---";
$obj = Class->new; print Dump $obj'
同じ(そしてこれとは無関係な)パーツが抑制された印刷
SV = IV(0x12d5530)at 0x12d5540 REFCNT = 1 フラグ=(韓国) RV = 0x12a5a68 SV = PVHV(0x12ab980)at 0x12a5a68 REFCNT = 1 フラグ=(SHAREKEYS) ... SV = IV(0x12a5ce0)at 0x12a5cf0 REFCNT = 1 フラグ=(IOK、pIOK) IV = 1 --- SV = IV(0x12cb8b8)at 0x12cb8c8 REFCNT = 1 フラグ=(PADMY、ROK) RV = 0x12c26b0 SV = PVHV(0x12aba00)at 0x12c26b0 REFCNT = 1 フラグ=(OBJECT、SHAREKEYS) STASH = 0x12d5300 "クラス" ... SV = IV(0x12c26b8)at 0x12c26c8 REFCNT = 1 フラグ=(IOK、pIOK) IV = 10
これで、1)オブジェクトである、2)どのパッケージに属しているのかがわかります。これにより、その使用法がわかります。
たとえば、その変数の逆参照が発生した場合($obj->name
)、その名前のサブルーチンがパッケージ(または階層)で検索され、オブジェクトが最初の引数として渡されます。
私はこの考えに従って、オブジェクト指向のPerl開発を導きました。
データ構造参照をクラスに関連付けます。Perlが(一種のツリーで)継承構造を作成する方法を考えると、オブジェクトモデルを利用して合成用のオブジェクトを作成するのは簡単です。
この関連付けでは、オブジェクトと呼ばれ、常に開発することは、オブジェクトの内部状態とクラスの動作が分離されていることを念頭に置いてください。そして、あらゆるデータ参照がパッケージ/クラスの振る舞いを使用することを祝福/許可することができます。パッケージはオブジェクトの「感情的な」状態を理解できるので。
たとえば、Bugオブジェクトが祝福されたハッシュになると確信できる場合は、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されたハッシュのさまざまなエントリにアクセスします。