Co dokładnie robi „błogosławieństwo” Perla?


142

Rozumiem, że słowo kluczowe „bless” w Perlu jest używane wewnątrz metody „nowej” klasy:

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

Ale co dokładnie robi „błogosławieństwo” z tym hashem?


2
Zobacz „Bless My Referents” z 1999 roku. Wygląda dość szczegółowo. ( Niestety, ręczny wpis Perla nie ma wiele do powiedzenia na ten temat.)
Jon Skeet,

Odpowiedzi:


143

Ogólnie rzecz biorąc, blesswiąże obiekt z klasą.

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

Teraz, kiedy wywołujesz metodę $object, Perl wie, w którym pakiecie szukać metody.

Jeśli drugi argument zostanie pominięty, jak w przykładzie, używany jest bieżący pakiet / klasa.

Ze względu na przejrzystość przykład można zapisać w następujący sposób:

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

EDYCJA: Zobacz dobrą odpowiedź kixx po trochę więcej szczegółów.


79

bless kojarzy odniesienie z pakietem.

Nie ma znaczenia, do czego jest odniesienie, może to być hash (najczęstszy przypadek), tablica (niezbyt często), skalar (zwykle oznacza to obiekt wywrócony na drugą stronę ), wyrażenie regularne , subroutine lub TYPEGLOB (zobacz książkę Object Oriented Perl: A Comprehensive Guide to Concepts and Programming Techniques autorstwa Damiana Conwaya, gdzie znajdziesz przydatne przykłady) lub nawet odniesienie do uchwytu pliku lub katalogu (najmniej powszechny przypadek).

Efektem blessjest to, że pozwala ci zastosować specjalną składnię do błogosławionego odniesienia.

Na przykład, jeśli błogosławione odniesienie jest przechowywane w $obj(skojarzone blessz pakietem "Klasa"), to $obj->foo(@args)wywoła podprogram fooi przekaże jako pierwszy argument odwołanie, $objpo którym nastąpi reszta argumentów ( @args). Podprogram należy zdefiniować w pakiecie „Class”. Jeśli nie ma podprogramu foow pakiecie „Class”, @ISAprzeszukana zostanie lista innych pakietów (wziętych z tablicy w pakiecie „Class”) i foozostanie wywołany pierwszy znaleziony podprogram .


16
Twoje początkowe stwierdzenie jest nieprawidłowe. Tak, błogosławieństwo przyjmuje odniesienie jako pierwszy argument, ale błogosławiona jest zmienna odniesienia, a nie samo odniesienie. $ perl -le 'sub Somepackage :: foo {42}; % h = (); $ h = \% h; bless $ h, "Somepackage"; $ j = \% h; print $ j-> UNIVERSAL :: can ("foo") -> () '42
converter42

1
Wyjaśnienie Kixx jest wyczerpujące. Nie powinniśmy zawracać sobie głowy wybieraniem przez konwerter teoretycznych drobiazgów.
Blessed Geek,

19
@Blessed Geek, to nie są teoretyczne drobiazgi. Różnica ma praktyczne zastosowanie.
ikegami

3
Stary odsyłacz perlfoundation.org do „obiektu na zewnątrz” znajduje się teraz w najlepszym przypadku za ścianą logowania. Link do Archive.org oryginału znajduje się tutaj .
ruffin

2
Być może posłuży to zamiast zepsutego linku @harmic skomentowane na: perldoc.perl.org/perlobj.html#Inside-Out-objects
Rhubbarb.

9

Wersja skrócona: oznacza ten skrót jako dołączony do przestrzeni nazw pakietu (tak, że pakiet zapewnia implementację swojej klasy).


7

Ta funkcja informuje jednostkę, do której odwołuje się REF, że jest teraz obiektem w pakiecie CLASSNAME lub bieżącym pakietem, jeśli pominięto CLASSNAME. Zalecane jest użycie dwuargumentowej formy błogosławieństwa.

Przykład :

bless REF, CLASSNAME
bless REF

Wartość zwracana

Ta funkcja zwraca odniesienie do obiektu przekazanego do CLASSNAME.

Przykład :

Poniżej znajduje się przykładowy kod pokazujący jego podstawowe użycie, odwołanie do obiektu jest tworzone przez błogosławieństwo odniesienia do klasy pakietu -

#!/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;
}

4

Udzielę odpowiedzi tutaj, ponieważ te tutaj nie kliknęły dla mnie.

Funkcja błogosławieństwa Perla kojarzy wszelkie odniesienia do wszystkich funkcji wewnątrz pakietu.

Po co nam to?

Zacznijmy od przedstawienia przykładu w 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
})();

Teraz usuńmy konstrukcję klasy i obejdźmy się bez niej:

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

Funkcja pobiera tablicę skrótów z nieuporządkowanymi właściwościami (ponieważ nie ma sensu pisać właściwości w określonej kolejności w językach dynamicznych w 2016 r.) I zwraca tabelę skrótów z tymi właściwościami lub jeśli zapomniałeś wstawić nowe słowo kluczowe, zwróci cały kontekst globalny (np. okno w przeglądarce lub globalny w nodejs).

Perl nie ma „tego”, „nowego” ani „klasy”, ale nadal może mieć funkcję, która zachowuje się podobnie. Nie będziemy mieli konstruktora ani prototypu, ale będziemy mogli dowolnie tworzyć nowe zwierzęta i modyfikować ich indywidualne właściwości.

# self contained scope 
(sub {
    my $Animal = (sub {
        return {
            'name' => $_[0]{'name'},
            'sound' => $_[0]{'sound'}
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    print $animal->{sound};
})->();

Teraz mamy problem: co by było, gdybyśmy chcieli, aby zwierzę samodzielnie odtwarzało dźwięki, zamiast drukować, jaki jest ich głos. Oznacza to, że chcemy, aby funkcja performSound wyświetlała dźwięk własnego zwierzęcia.

Jednym ze sposobów jest nauczenie każdego zwierzęcia, jak ma robić to, co słychać. Oznacza to, że każdy kot ma swoją zduplikowaną funkcję do wykonywania dźwięku.

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

Jest to złe, ponieważ performSound jest umieszczany jako zupełnie nowy obiekt funkcji za każdym razem, gdy konstruowane jest zwierzę. 10000 zwierząt to 10000 wykonanych dźwięków. Chcemy, aby jedna funkcja performSound była używana przez wszystkie zwierzęta, które wyszukują własne dźwięki i je drukują.

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

Tutaj kończy się paralela do Perla.

Nowy operator JavaScript nie jest opcjonalny, bez niego „this” wewnątrz metod obiektu psuje globalny zasięg:

(() => {
    // '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.

})();

Chcemy mieć jedną funkcję dla każdego zwierzęcia, która wyszukuje własny dźwięk tego zwierzęcia, zamiast zakodować go na sztywno w konstrukcji.

Błogosławieństwo pozwala nam używać pakietu jako prototypu obiektów. W ten sposób obiekt jest świadomy „pakietu”, do którego „się odwołuje”, i z kolei może mieć funkcje w pakiecie „sięgać” do określonych instancji, które zostały utworzone na podstawie konstruktora tego „obiektu pakietu”:

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

Podsumowanie / TL; DR :

Perl nie ma „tego”, „klasy” ani „nowego”. pobłogosławienie obiektu pakietowi daje temu obiektowi odniesienie do pakietu, a kiedy wywołuje funkcje w pakiecie, ich argumenty zostaną przesunięte o 1 szczelinę, a pierwszy argument ($ _ [0] lub shift) będzie równoważny javascript's „this”. Możesz z kolei nieco symulować prototypowy model JavaScript.

Niestety uniemożliwia to (w moim rozumieniu) tworzenie "nowych klas" w czasie wykonywania, ponieważ każda "klasa" musi mieć swój własny pakiet, podczas gdy w javascript w ogóle nie potrzebujesz pakietów, jako słowo kluczowe "nowe" tworzy anonimowy skrót, którego możesz używać jako pakietu w czasie wykonywania, do którego możesz dodawać nowe funkcje i usuwać funkcje w locie.

Istnieją biblioteki Perla, które tworzą własne sposoby pokonywania tego ograniczenia ekspresji, na przykład Moose.

Dlaczego zamieszanie? :

Z powodu pakietów. Nasza intuicja podpowiada nam, aby związać obiekt z hasmapą zawierającą jego prototyp. To pozwala nam tworzyć „pakiety” w czasie wykonywania, jak JavaScript. Perl nie ma takiej elastyczności (przynajmniej nie jest wbudowany, musisz go wymyślić lub pobrać z innych modułów), co z kolei utrudnia ekspresję w środowisku uruchomieniowym. Nazywanie tego „błogosławieństwem” również nie przynosi zbyt wielu korzyści.

Co chcemy zrobić :

Coś podobnego do tego, ale mają powiązanie z mapą prototypu rekurencyjnie i są niejawnie powiązane z prototypem, zamiast robić to jawnie.

Oto naiwna próba: problem polega na tym, że „call” nie wie „co go wywołało”, więc równie dobrze może to być uniwersalna funkcja perla „objectInvokeMethod (obiekt, metoda)”, która sprawdza, czy obiekt ma metodę , albo ma go jego prototyp, albo ma go jego prototyp, dopóki nie dotrze do końca i go nie znajdzie (dziedziczenie prototypowe). Perl ma do tego niezłą magię ewaluacyjną, ale zostawię to na coś, co będę mógł zrobić później.

Tak czy inaczej, oto pomysł:

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

Miejmy nadzieję, że ktoś uzna ten post za przydatny.


Tworzenie nowych klas w czasie wykonywania nie jest niemożliwe. my $o = bless {}, $anything;pobłogosławi obiekt w $anythingklasie. Podobnie {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};utworzy metodę o nazwie „somesub” w klasie o nazwie w $anything. To wszystko jest możliwe w czasie wykonywania. Jednak „możliwe” nie oznacza, że ​​posługiwanie się w codziennym kodzie jest świetną praktyką. Ale jest przydatny w budowaniu systemów nakładania obiektów, takich jak Moose lub Moo.
DavidO

interesujące, więc mówisz, że mogę pobłogosławić desygnat klasy, której nazwa jest określana w czasie wykonywania. Wydaje się interesujące i unieważnia moje unfortunately it makes it impossible(to my understanding) to create "new classes" at runtimeroszczenie. Wydaje mi się, że moje obawy ostatecznie sprowadziły się do tego, że manipulowanie / introspekcja systemu pakietów w czasie wykonywania jest znacznie mniej intuicyjne, ale jak dotąd nie udało mi się pokazać niczego, czego z natury nie może zrobić. Wydaje się, że system pakietów obsługuje wszystkie narzędzia potrzebne do dodawania / usuwania / sprawdzania / modyfikowania samego siebie w czasie wykonywania.
Dmitry

To jest poprawne; możesz programowo manipulować tablicą symboli Perla, a zatem możesz manipulować pakietami Perla i składnikami pakietu w czasie wykonywania, nawet bez zadeklarowania „pakietu Foo” w dowolnym miejscu. Przeglądanie i manipulowanie tabelą symboli w czasie wykonywania, semantyka AUTOLOAD, atrybuty podprogramów, wiązanie zmiennych z klasami ... istnieje wiele sposobów, aby dostać się pod maskę. Niektóre z nich są przydatne do automatycznego generowania interfejsów API, narzędzi walidacyjnych, interfejsów API do automatycznego dokumentowania; nie możemy przewidzieć wszystkich przypadków użycia. Możliwym skutkiem takiego podstępu jest też strzelenie sobie w stopę.
DavidO

4

Oprócz wielu dobrych odpowiedzi, tym, co szczególnie wyróżnia blessodwołanie z a -ed, jest to, że SV dla niego pobiera dodatkowe FLAGS( OBJECT) iSTASH

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'

Wydruki z wyłączonymi tymi samymi (i nieistotnymi do tego) częściami

SV = IV (0x12d5530) pod adresem 0x12d5540
  REFCNT = 1
  FLAGI = (ROK)
  RV = 0x12a5a68
  SV = PVHV (0x12ab980) pod adresem 0x12a5a68
    REFCNT = 1
    FLAGI = (SHAREKEYS)
    ...
      SV = IV (0x12a5ce0) przy 0x12a5cf0
      REFCNT = 1
      FLAGI = (IOK, pIOK)
      IV = 1
---
SV = IV (0x12cb8b8) przy 0x12cb8c8
  REFCNT = 1
  FLAGI = (PADMY, ROK)
  RV = 0x12c26b0
  SV = PVHV (0x12aba00) pod adresem 0x12c26b0
    REFCNT = 1
    FLAGI = (OBIEKT, SHAREKEYS)
    STASH = 0x12d5300 "Klasa"
    ...
      SV = IV (0x12c26b8) pod adresem 0x12c26c8
      REFCNT = 1
      FLAGI = (IOK, pIOK)
      IV = 10

Dzięki temu wiadomo, że 1) jest obiektem, 2) do jakiego pakietu należy, a to informuje o jego użyciu.

Na przykład, gdy napotkano wyłuskiwanie tej zmiennej ( $obj->name), podrzędna nazwa o tej nazwie jest poszukiwana w pakiecie (lub hierarchii), obiekt jest przekazywany jako pierwszy argument itp.


1

I Zgodnie z tą myślą, aby poprowadzić programowanie zorientowanego obiektowo Perla.

Błogosławieństwo skojarz dowolne odniesienie do struktury danych z klasą. Biorąc pod uwagę, w jaki sposób Perl tworzy strukturę dziedziczenia (w rodzaju drzewa), łatwo jest wykorzystać model obiektowy do tworzenia obiektów do kompozycji.

Do tego skojarzenia nazwaliśmy obiekt, aby rozwijać zawsze mając na uwadze, że stan wewnętrzny obiektu i zachowania klasowe są rozdzielone. Możesz też pobłogosławić / zezwolić dowolnemu odwołaniu do danych na użycie dowolnego zachowania pakietu / klasy. Ponieważ opakowanie może zrozumieć „emocjonalny” stan obiektu.


Oto te same ogłoszenia dotyczące tego, jak Perl działa z przestrzeniami nazw pakietów i jak działa ze stanami zarejestrowanymi w Twojej przestrzeni nazw. Ponieważ istnieją pragmaty, takie jak use namespace :: clean. Ale postaraj się, aby wszystko było prostsze.
Steven Koch

-9

Na przykład, jeśli możesz być pewien, że jakikolwiek obiekt Bug będzie błogosławionym hashem, możesz (w końcu!) Uzupełnić brakujący kod w metodzie 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";
 }

Teraz, ilekroć metoda print_me jest wywoływana przez odniesienie do dowolnego skrótu, który został pobłogosławiony w klasie Bug, zmienna $ self wyodrębnia referencję, która została przekazana jako pierwszy argument, a następnie instrukcje print uzyskują dostęp do różnych wpisów błogosławionego skrótu.


@darch Z jakiego źródła ta odpowiedź została plagiatowana?
Anderson Green,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.