문제
클래스의 "새"메소드 내에서 Perl에서 "축복"키워드를 사용한다는 것을 이해합니다.
sub new {
my $self = bless { };
return $self;
}
그러나 그 해시 참조에 대해 "축복"이 정확히 무엇을하고 있습니까?
해결책
일반적으로 bless
객체를 클래스와 연관시킵니다.
package MyClass;
my $object = { };
bless $object, "MyClass";
이제 메소드를 호출 할 때 $object
, Perl은 메소드를 검색 할 패키지를 알고 있습니다.
예제에서와 같이 두 번째 인수가 생략되면 현재 패키지/클래스가 사용됩니다.
명확성을 위해 예제는 다음과 같이 작성 될 수 있습니다.
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
다른 팁
bless
참조를 패키지와 연관시킵니다.
참조가 무엇이든, 해시 (가장 일반적인 경우), 배열 (일반적이지 않음), 스칼라 (일반적으로 이것은 내부 객체), 정규 표현, 서브 루틴 또는 타이프 로브 (책 참조) 객체 지향적 Perl : Damian Conway의 개념 및 프로그래밍 기술에 대한 포괄적 인 안내서 유용한 예제의 경우) 또는 파일 또는 디렉토리 핸들에 대한 참조 (최소한의 경우).
그 효과 bless
-ing은 축복받은 참조에 특수 구문을 적용 할 수 있다는 것입니다.
예를 들어, 축복받은 참조가 $obj
(관련 bless
그런 다음 패키지 "클래스") $obj->foo(@args)
서브 루틴을 호출합니다 foo
그리고 첫 번째 인수로 참조를 통과하십시오 $obj
나머지 논쟁이 뒤 따른다 (@args
). 서브 루틴은 패키지 "클래스"로 정의되어야합니다. 서브 루틴이없는 경우 foo
패키지 "클래스"에서 다른 패키지 목록 (배열을 찍는 것) @ISA
패키지 "클래스")에서 검색하고 첫 번째 서브 루틴이 foo
발견됩니다.
짧은 버전 : 현재 패키지 네임 스페이스에 첨부 된 해시가 표시되어 해당 패키지가 클래스 구현을 제공합니다).
이 기능은 Ref가 참조 한 엔티티에게 이제 ClassName 패키지의 개체 또는 ClassName이 생략 된 경우 현재 패키지임을 알려줍니다. 두 가지 변론 형태의 축복을 사용하는 것이 권장됩니다.
예시:
bless REF, CLASSNAME
bless REF
반환 값
이 함수는 축복받은 객체에 대한 참조를 클래스 이름으로 반환합니다.
예시:
다음은 기본 사용법을 보여주는 예제 코드이며, 객체 참조는 패키지의 클래스에 대한 참조를 축복하여 생성됩니다.
#!/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 Function은 패키지 내부의 모든 기능에 대한 참조를 연관시킵니다.
왜 우리는 이것을 필요로합니까?
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 년에 동적 언어로 특정 순서로 속성을 작성할 필요가 없기 때문에) 해당 속성으로 해시 테이블을 반환하거나 새 키워드를 넣는 것을 잊어 버린 경우 IT. 전체 글로벌 컨텍스트 (예 : 브라우저 또는 Nodejs의 글로벌)를 반환합니다.
Perl에는 "이"또는 "새로운"또는 "클래스"가 없지만 여전히 비슷하게 작동하는 함수가있을 수 있습니다. 우리는 생성자 나 프로토 타입을 가지고 있지 않지만, 마음대로 새로운 동물을 만들고 개별 특성을 수정할 수 있습니다.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
이제 우리는 문제가 있습니다. 우리가 동물이 그들의 목소리가 무엇인지 인쇄하는 대신 스스로 소리를 수행하기를 원한다면 어떻게해야합니까? 즉, 우리는 동물의 소리를 인쇄하는 기능을 수행하기를 원합니다.
이를 수행하는 한 가지 방법은 각 개별 동물에게 소리를내는 방법을 가르치는 것입니다. 이것은 각 고양이가 성능에 대한 고유 한 중복 함수를 가지고 있음을 의미합니다.
# 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'}();
})->();
Performound는 동물을 구성 할 때마다 완전히 새로운 기능 객체로 배치되기 때문에 이것은 나쁩니다. 10000 마리의 동물은 100000 성능을 의미합니다. 우리는 자신의 사운드를 찾고 인쇄하는 모든 동물이 사용하는 단일 기능 성능을 원합니다.
(() => {
'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의 새로운 연산자는 선택 사항이 아니며, "이"내부 객체 방법은 글로벌 범위를 손상시킵니다.
(() => {
// '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)는 동일합니다. JavaScript의 "This". 차례로 JavaScript의 프로토 타입 모델을 다소 시뮬레이션 할 수 있습니다.
불행히도 런타임에 "새로운 클래스"를 만드는 것은 불가능합니다. 각 "클래스"가 자체 패키지를 갖기 위해 필요하기 때문에 자바 스크립트에서는 "새로운"키워드로 패키지가 전혀 필요하지 않습니다. 런타임에 패키지로 사용할 수 있도록 익명의 해시 맵을 구성하여 새로운 기능을 추가하고 즉시 함수를 제거 할 수 있습니다.
무스와 같은 표현력 에서이 한계를 연결하는 자체 방법을 만드는 일부 Perl 라이브러리가 있습니다.
왜 혼란?:
패키지 때문에. 우리의 직관은 우리에게 객체를 프로토 타입을 포함하는 해시 맵에 바인딩하도록 지시합니다. 이를 통해 JavaScript Can과 같이 런타임에 "패키지"를 만들 수 있습니다. 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'));
})->();
어쨌든 누군가 가이 게시물이 유용하다는 것을 알기를 바랍니다.
나는이 생각을 따라 개발 객체 지향적 perl을 안내합니다.
축복 데이터 구조 참조를 클래스와 연관시킵니다. Perl이 상속 구조를 만드는 방법 (트리의 일종)을 감안할 때 객체 모델을 활용하여 구성을위한 객체를 만들기 쉽습니다.
이 연관성을 위해 우리는 객체라고 불렀으며, 발달하기 위해 항상 객체와 클래스 행동의 내부 상태가 분리되어 있음을 명심해야합니다. 또한 모든 데이터 참조가 패키지/클래스 동작을 사용하도록 축복/허용 할 수 있습니다. 패키지는 물체의 "정서적"상태를 이해할 수 있기 때문에.
예를 들어, 버그 객체가 축복받은 해시가 될 것이라고 확신 할 수 있다면 버그 :: 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 메소드가 버그 클래스에 축복 된 해시에 대한 참조를 통해 호출 될 때마다 $ Self Variable은 첫 번째 인수로 전달 된 참조를 추출한 다음 Print 문은 축복받은 해시의 다양한 항목에 액세스합니다.