IT박스

펄의“축복”은 정확히 무엇을 하는가?

itboxs 2020. 6. 24. 07:50
반응형

펄의“축복”은 정확히 무엇을 하는가?


클래스의 "새"메소드 내에서 Perl에서 "축복"키워드를 사용하는 것으로 알고 있습니다.

sub new {
    my $self = bless { };
    return $self;
}    

그러나 해시 참조에 정확히 "축복"을하는 것은 무엇입니까?


일반적으로 bless객체를 클래스와 연결합니다.

package MyClass;
my $object = { };
bless $object, "MyClass";

이제 on 메소드를 호출 할 때 $objectPerl은 메소드를 검색 할 패키지를 알고 있습니다.

예제와 같이 두 번째 인수를 생략하면 현재 패키지 / 클래스가 사용됩니다.

명확성을 기하기 위해 다음과 같이 예제를 작성할 수 있습니다.

sub new { 
  my $class = shift; 
  my $self = { }; 
  bless $self, $class; 
} 

편집 : 좀 더 자세한 내용은 kixx 의 좋은 답변참조하십시오 .


bless 참조를 패키지와 연관시킵니다.

참조가 무엇인지는 중요하지 않습니다. 해시 (가장 일반적인 경우), 배열 (그렇지 않은), 스칼라 (일반적으로 이것은 내부 객체를 나타냄), 정규 표현식에 대한 것일 수 있습니다 , 서브 루틴 또는 TYPEGLOB ( 유용한 예제는 Object Oriented Perl : Damian Conway의 개념 및 프로그래밍 기술에 대한 종합 안내서 참조) 또는 파일 또는 디렉토리 핸들에 대한 참조 (최소 공통 사례)를 참조하십시오.

그 효과 bless는 복된 참조에 특별한 구문을 적용 할 수 있다는 것입니다.

예를 들어, 복된 참조가 저장되어 있으면 $obj( bless"클래스"패키지와 연관 됨 ) $obj->foo(@args)서브 루틴을 호출하고 foo첫 번째 인수를 참조 $obj다음에 나머지 인수 ( @args)가 전달합니다. 서브 루틴은 "클래스"패키지에 정의되어야합니다. foo"Class"패키지에 서브 루틴이없는 경우, "Class"패키지의 배열 @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.

})();

우리는 건설 할 때 하드 코딩하지 않고 그 동물의 소리를 찾는 각 동물에 대해 하나의 기능을 원합니다.

Blessing lets us use a package as the prototype of objects. This way, the object is aware of the "package" it is "referenced to", and in turn can have the functions in the package "reach into" the specific instances that were created from the constructor of that "package object":

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();

Summary/TL;DR:

Perl has no "this", "class", nor "new". blessing an object to a package gives that object a reference to the package, and when it calls functions in the package, their arguments will be offset by 1 slot, and the first argument($_[0] or shift) will be equivalent to javascript's "this". In turn, you can somewhat simulate JavaScript's prototype model.

Unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime, as you need each "class" to have its own package, whereas in javascript, you don't need packages at all, as "new" keyword makes up an anonymous hashmap for you to use as a package at runtime to which you can add new functions and remove functions on the fly.

There are some Perl libraries creating their own ways of bridging this limitation in expressiveness, such as Moose.

Why the confusion?:

Because of packages. Our intuition tells us to bind the object to a hashmap containing its' prototype. This lets us create "packages" at runtime like JavaScript can. Perl does not have such flexibility(at least not built in, you have to invent it or get it from other modules), and in turn your runtime expressiveness is hindered. Calling it "bless" doesn't do it much favors neither.

What we want to do:

Something like this, but have binding to the prototype map recursive, and be implicitly bound to the prototype rather than having to explicitly do it.

Here is a naive attempt at it: the issue is that "call" does not know "what called it", so it may as well be a universal perl function "objectInvokeMethod(object, method)" which checks whether the object has the method, or its prototype has it, or its prototype has it, until it reaches the end and finds it or not (prototypical inheritence). Perl has nice eval magic to do it but I'll leave that for something I can try doing later.

Anyway here is the idea:

(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'));
})->();

Anyway hopefully somebody will find this post useful.


I Following this thought to guide the development object-oriented Perl.

Bless associate any data structure reference with a class. Given how Perl creates the inheritance structure (in a kind of tree) it is easy to take advantage of the object model to create Objects for composition.

For this association we called object, to develop always have in mind that the internal state of the object and class behaviours are separated. And you can bless/allow any data reference to use any package/class behaviours. Since the package can understand "the emotional" state of the object.


For example, if you can be confident that any Bug object is going to be a blessed hash, you can (finally!) fill in the missing code in the Bug::print_me method:

 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";
 }

Now, whenever the print_me method is called via a reference to any hash that's been blessed into the Bug class, the $self variable extracts the reference that was passed as the first argument and then the print statements access the various entries of the blessed hash.

참고URL : https://stackoverflow.com/questions/392135/what-exactly-does-perls-bless-do

반응형