답변:
일반적으로 bless
객체를 클래스와 연결합니다.
package MyClass;
my $object = { };
bless $object, "MyClass";
이제 on 메소드를 호출하면 $object
Perl은 메소드를 검색 할 패키지를 알고 있습니다.
예제에서와 같이 두 번째 인수를 생략하면 현재 패키지 / 클래스가 사용됩니다.
명확성을 기하기 위해 예제는 다음과 같이 작성 될 수 있습니다.
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
bless
참조를 패키지와 연관시킵니다.
참조가 무엇인지는 중요하지 않습니다. 해시 (가장 일반적인 경우), 배열 (그렇지 않은), 스칼라 (일반적으로 이것은 내부 객체를 나타냄), 정규 표현식에 대한 것일 수 있습니다 , 서브 루틴 또는 TYPEGLOB ( 유용한 예제는 Object Oriented Perl : Damian Conway의 개념 및 프로그래밍 기술에 대한 종합 안내서 참조) 또는 파일 또는 디렉토리 핸들에 대한 참조 (최소 공통 사례)를 참조하십시오.
그 효과 bless
는 복된 참조에 특별한 구문을 적용 할 수 있다는 것입니다.
예를 들어, 복된 참조가 저장되어 있으면 $obj
( bless
"클래스"패키지와 연관 됨 ) $obj->foo(@args)
서브 루틴을 호출하고 foo
첫 번째 인수를 참조 $obj
다음에 나머지 인수 ( @args
)가 전달합니다. 서브 루틴은 "클래스"패키지에 정의되어야합니다. foo
"클래스"패키지에 서브 루틴이 없으면 다른 패키지 목록 ( @ISA
"클래스"패키지 의 배열 에서 가져온 )이 검색되고 foo
발견 된 첫 번째 서브 루틴 이 호출됩니다.
이 함수는 REF가 참조하는 엔티티에게 CLASSNAME 패키지의 오브젝트이거나 CLASSNAME이 생략 된 경우 현재 패키지임을 알려줍니다. 2 인수 형식의 축복을 사용하는 것이 좋습니다.
예 :
bless REF, CLASSNAME
bless REF
반환 값
이 함수는 CLASSNAME에 축복 된 객체에 대한 참조를 반환합니다.
예 :
다음은 기본 사용법을 보여주는 예제 코드입니다. 패키지 클래스에 대한 참조를 축복하여 객체 참조를 만듭니다.
#!/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 함수가 필요합니다.
이를 수행하는 한 가지 방법은 각 동물에게 소리를내는 방법을 가르치는 것입니다. 즉, 각 Cat에는 소리를 수행하기위한 고유 한 복제 기능이 있습니다.
# 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의 새로운 연산자는 선택 사항이 아니며 객체 메소드 내부의 "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.
})();
우리는 건설 할 때 하드 코딩하지 않고 그 동물의 소리를 찾는 각 동물에 대해 하나의 기능을 원합니다.
축복을 통해 패키지를 객체의 프로토 타입으로 사용할 수 있습니다. 이런 방식으로 객체는 "참조 된" "패키지"를 인식하고 패키지의 함수가 해당 "패키지 객체"의 생성자에서 생성 된 특정 인스턴스에 "도달"할 수 있습니다.
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"가 없습니다. 패키지에 객체를 축복하면 해당 객체에 패키지에 대한 참조가 제공되고 패키지에서 함수를 호출 할 때 인수가 1 슬롯만큼 오프셋되고 첫 번째 인수 ($ _ [0] 또는 shift)는 다음과 같습니다. 자바 스크립트의 "this". 결과적으로 JavaScript 프로토 타입 모델을 다소 시뮬레이션 할 수 있습니다.
불행히도 런타임에 "새 클래스"를 만드는 것은 불가능합니다. 각 "클래스"에는 자체 패키지가 있어야하지만 자바 스크립트에서는 패키지가 전혀 필요하지 않습니다. 새로운 함수를 추가하고 즉시 함수를 제거 할 수있는 런타임에 패키지로 사용할 수 있도록 익명의 해시 맵을 구성합니다.
무스 (Mose)와 같은 표현성의 한계를 극복하는 고유 한 방식을 만드는 일부 Perl 라이브러리가 있습니다.
왜 혼란? :
패키지로 인해. 직관은 객체를 프로토 타입이 포함 된 해시 맵에 바인딩하도록 지시합니다. 이를 통해 JavaScript와 같이 런타임에 "패키지"를 만들 수 있습니다. Perl은 그러한 유연성을 갖지 못하고 (적어도 내장되어 있지 않거나, 그것을 개발하거나 다른 모듈에서 가져와야 함) 런타임 표현력이 저해됩니다. 그것을 "축복"이라고 부르는 것도 그다지 호의적이지 않습니다.
우리가하고 싶은 것 :
이와 같은 것이지만 프로토 타입 맵을 재귀 적으로 바인딩하고 명시 적으로 수행하지 않고 암시 적으로 프로토 타입에 바인딩해야합니다.
여기에는 순진한 시도가 있습니다. 문제는 "호출"이 "호출"을 모르기 때문에 객체에 메소드가 있는지 확인하는 범용 perl 함수 "objectInvokeMethod (object, method)"일 수도 있습니다. 또는 프로토 타입이 있거나 끝에 도달하여 찾을 수있을 때까지 (프로토 타입 상속) 프로토 타입이 있습니다. Perl은 훌륭한 평가 마법을 가지고 있지만 나중에 시도해 볼 수있는 것을 남겨 두겠습니다.
어쨌든 여기 아이디어가 있습니다.
(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 = 0x12d5540에서 IV (0x12d5530) REFCNT = 1 플래그 = (ROK) RV = 0x12a5a68 SV = 0x12a5a68에서 PVHV (0x12ab980) REFCNT = 1 플래그 = (SHAREKEYS) ... SV = 0x12a5cf0에서 IV (0x12a5ce0) REFCNT = 1 플래그 = (IOK, pIOK) IV = 1 --- SV = 0x12cb8c8에서 IV (0x12cb8b8) REFCNT = 1 플래그 = (PADMY, ROK) RV = 0x12c26b0 SV = 0x12c26b0에서 PVHV (0x12aba00) REFCNT = 1 플래그 = (OBJECT, SHAREKEYS) STASH = 0x12d5300 "클래스" ... SV = 0x12c26c8에서 IV (0x12c26b8) 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";
}
이제 print_me 메소드가 Bug 클래스에 축복 된 해시에 대한 참조를 통해 호출 될 때마다 $ self 변수는 첫 번째 인수로 전달 된 참조를 추출한 다음 print 문은 축복받은 해시의 다양한 항목에 액세스합니다.