Що саме робить «благословення» Перла?


142

Я розумію, що в Perl використовується ключове слово "благословити" всередині "нового" методу класу:

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

Але що саме "благословляє" робити на це хеш-посилання?


2
Дивіться "Благословляйте моїх референтів" від 1999 року. Виглядає досить детально. (На жаль, ручний запис Perl , на жаль, не має про що сказати.)
Джон Скіт,

Відповіді:


143

Взагалі blessасоціює об’єкт із класом.

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

Тепер, коли ви викликаєте метод $object, Perl дізнається, в якому пакеті шукати метод.

Якщо другий аргумент опущено, як у вашому прикладі, використовується поточний пакет / клас.

Для наочності ваш приклад може бути записаний так:

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

EDIT: Дивіться хорошу відповідь kixx для отримання більш детальної інформації.


79

bless пов'язує посилання з пакетом.

Неважливо, на що посилається, це може бути хеш (найчастіший випадок), масив (не настільки поширений), скаляр (зазвичай це вказує на об'єкт зсередини ), на регулярний вираз , підпрограма або TYPEGLOB (див. книгу « Орієнтований на об’єкт Perl: всебічний посібник з концепцій та методик програмування Даміана Конвей» для корисних прикладів) або навіть посилання на файл або каталог ручки (найменш поширений випадок).

Ефект blessполягає в тому, що він дозволяє застосувати спеціальний синтаксис до блаженної посилання.

Наприклад, якщо блаженна посилання зберігається в $obj(пов’язаному blessз пакетом "Клас"), то $obj->foo(@args)викличе підпрограму fooі передасть в якості першого аргументу посилання, $objа потім решту аргументів ( @args). Підпрограма повинна бути визначена в пакеті "Клас". Якщо fooв пакеті "Клас" немає підпрограми, буде шуканий список інших пакетів (узятих із масиву @ISAв пакеті "Клас"), і fooбуде викликано першу знайдену підпрограму .


16
Ваше початкове твердження невірне. Так, bless сприймає посилання як перший аргумент, але блаженна саме референтна зміна, а не сама посилання. $ perl -le 'sub Somepackage :: foo {42}; % h = (); $ h = \% h; благословити $ h, "Somepackage"; $ j = \% h; print $ j-> UNIVERSAL :: can ("foo") -> () '42
конвертер42

1
Пояснення Кікса є вичерпним. Ми не повинні турбуватися з вибором перетворювача в теоретичних деталях.
Блаженний Geek

19
@ Блаженний Geek, це не теоретичні деталі. Різниця має практичне застосування.
ikegami

3
Старе посилання perlfoundation.org для "об'єкта зсередини" зараз знаходиться в кращому випадку за стіною входу. Тут розміщено посилання на оригінал Archive.org .
ruffin

2
Можливо, це послужить замість розірваного посилання @harmic прокоментував: perldoc.perl.org/perlobj.html#Inside-Out-objects
Rhubbarb

9

Коротка версія: це маркування хеша як прикріпленого до поточного простору імен пакунків (так що цей пакет забезпечує його виконання класу).


7

Ця функція повідомляє об'єкту, на який посилається REF, що тепер це об'єкт у пакеті CLASSNAME або поточний пакет, якщо CLASSNAME опущено. Використовувати двоаргументальну форму благословення.

Приклад :

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

4

Я дам тут відповідь, оскільки ті, хто тут, не дуже натискали на мене.

Функція благословення Perl пов'язує будь-які посилання на всі функції всередині пакета.

Навіщо нам це потрібно?

Почнемо з викладу прикладу в 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 немає "цього", ані "нового", ані "класу", але він все одно може мати функцію, яка поводиться аналогічно. У нас не буде ні конструктора, ні прототипу, але ми зможемо створити нових тварин за бажанням та змінити їхні індивідуальні властивості.

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

Це погано, оскільки 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
})();

Ось де зупиняється паралель Перладу.

Новий оператор 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 немає ні "цього", "класу", ні "нового". благословляючи об’єкт на пакет, він дає цьому об'єкту посилання на пакет, і коли він викликає функції в пакеті, їхні аргументи будуть зміщені на 1 слот, і перший аргумент ($ _ [0] або зсув) буде еквівалентний JavaScript "це". У свою чергу, ви можете дещо імітувати модель прототипу JavaScript.

На жаль, це неможливо (наскільки я розумію) створювати "нові класи" під час виконання, оскільки для кожного "класу" потрібен власний пакет, тоді як у javascript вам пакетів взагалі не потрібен, як "нове" ключове слово складає анонімну хеш-карту, яку ви можете використовувати в якості пакету під час виконання, до якого ви можете додавати нові функції та видаляти функції на ходу.

Є кілька бібліотек Perl, які створюють власні способи усунення цього обмеження у виразності, наприклад, Moose.

Чому плутанина? :

Через пакети. Наша інтуїція підказує нам прив’язати об'єкт до хешмапу, що містить його 'прототип. Це дозволяє нам створювати "пакети" під час виконання, як JavaScript. Perl не має такої гнучкості (принаймні не вбудований, ви повинні його винайти або отримати з інших модулів), і в свою чергу виразність виконання буде перешкоджати. Називаючи це "благословити", це не сприяє і жодному.

Що ми хочемо зробити :

Щось подібне, але вони мають прив'язку до карти прототипу рекурсивним і повинні бути неявно прив'язані до прототипу, а не явно робити це.

Ось наївна спроба в цьому: проблема полягає в тому, що "виклик" не знає "що його викликало", тож цілком може бути універсальна функція perl "objectInvokeMethod (об'єкт, метод)", яка перевіряє, чи об'єкт має метод або його прототип має, або прототип має його, поки не досягне кінця і не знайде його чи ні (прототипічне успадкування). У 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.
DavidO

цікаво, тому ви говорите, що я можу поблагословити референта в класі, ім'я якого визначається під час виконання. Здається, це цікаво і нічим не скасовує мою unfortunately it makes it impossible(to my understanding) to create "new classes" at runtimeпретензію. Я думаю, що моє занепокоєння в кінцевому рахунку зводилося до того, що він був значно менш інтуїтивним для маніпулювання / самоаналізу пакетної системи під час виконання, але поки що я не спромігся показати щось, що по суті не може зробити. Пакетна система, здається, підтримує всі інструменти, необхідні для додавання / видалення / перевірки / модифікації під час виконання.
Дмитро

Це вірно; ви можете маніпулювати таблицею символів Perl програмно, а тому можете маніпулювати пакетами Perl та членами пакету під час виконання, навіть не декларувавши "пакет Foo" ніде. Огляд таблиці символів та маніпулювання під час виконання, семантика AUTOLOAD, атрибути підпрограми, прив'язка змінних до класів ... Є багато способів потрапити під капот. Деякі з них корисні для автоматичної генерації API, інструментів перевірки, API автодокументування; ми не можемо передбачити всіх випадків використання. Стрілянина в ногу - також можливий результат такої хитрості.
DavidO

4

Поряд з низкою хороших відповідей, що особливо вирізняється blessaed-посиланням, є те, що 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) при 0x12d5540
  REFCNT = 1
  ФЛАГИ = (ROK)
  RV = 0x12a5a68
  SV = PVHV (0x12ab980) при 0x12a5a68
    REFCNT = 1
    ФЛАГИ = (SHAREKEYS)
    ...
      SV = IV (0x12a5ce0) при 0x12a5cf0
      REFCNT = 1
      ФЛАГИ = (IOK, pIOK)
      IV = 1
---
SV = IV (0x12cb8b8) при 0x12cb8c8
  REFCNT = 1
  ФЛАГИ = (PADMY, ROK)
  RV = 0x12c26b0
  SV = PVHV (0x12aba00) при 0x12c26b0
    REFCNT = 1
    ФЛАГИ = (ОБ'ЄКТ, ШАРЕКІЇ)
    STASH = 0x12d5300 "Клас"
    ...
      SV = IV (0x12c26b8) при 0x12c26c8
      REFCNT = 1
      ФЛАГИ = (IOK, pIOK)
      IV = 10

З цим відомо, що 1) це об'єкт 2) до якого пакета належить, і це повідомляє про його використання.

Наприклад, коли зустрічається перенаправлення на цю змінну ( $obj->name), в пакеті (або ієрархії) шукається суб з цим ім'ям, об'єкт передається як перший аргумент тощо.


1

Я, дотримуючись цієї думки, керував розробкою об'єктно-орієнтованого Perl.

Благословіть асоціювання будь-яких посилань на структуру даних з класом. З огляду на те, як Perl створює структуру спадкування (у вигляді дерева), легко скористатися об'єктною моделлю для створення об'єктів для композиції.

Для цього об'єднання ми назвали об'єкт, щоб розвиватися завжди маючи на увазі, що внутрішній стан поведінки об'єкта та класу є розділеними. І ви можете благословити / дозволити будь-які посилання на дані використовувати будь-які поведінки пакунків / класів. Оскільки в пакеті можна зрозуміти "емоційний" стан об'єкта.


Ось такі ж анонси, як Perl працює з просторами імен пакетів і як працює зі станами, зареєстрованими у вашій області імен. Оскільки такі існують такі прагми, як використання простору імен :: clean. Але намагайся зробити речі простішими можливими.
Стівен Кох

-9

Наприклад, якщо ви можете бути впевнені, що будь-який об’єкт 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 вилучає посилання, яке було передано в якості першого аргументу, а потім оператори друку отримують доступ до різних записів блаженного хеша.


@darch З якого джерела плагіатували цю відповідь?
Андерсон Грін,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.