Что именно делает Perl "благослови"? - PullRequest
134 голосов
/ 24 декабря 2008

Я понимаю, что в Perl используется ключевое слово "bless" внутри метода "new" класса:

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

Но что именно "благословит" делать с этой ссылкой на хэш?

Ответы [ 7 ]

137 голосов
/ 24 декабря 2008

Обычно bless связывает объект с классом.

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

Теперь, когда вы вызываете метод на $object, Perl знает, какой пакет искать для метода.

Если второй аргумент опущен, как в вашем примере, используется текущий пакет / класс.

Ради ясности ваш пример может быть записан следующим образом:

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

РЕДАКТИРОВАТЬ: См. kixx хорошо ответ для более подробной информации.

77 голосов
/ 24 декабря 2008

bless связывает ссылку с пакетом.

Неважно, на что ссылается, это может быть хеш (наиболее распространенный случай), массив (не очень распространенный), скаляр (обычно это указывает на объект наизнанку) ), регулярному выражению, подпрограмме или TYPEGLOB (см. Книгу Объектно-ориентированный Perl: всеобъемлющее руководство по концепциям и методам программирования Дамиана Конвея для полезных примеров) или даже ссылку на файл или дескриптор каталога (наименее распространенный случай).

Эффект, который имеет bless, заключается в том, что он позволяет применять специальный синтаксис к благословенной ссылке.

Например, если благословенная ссылка хранится в $obj (связанном bless с пакетом «Класс»), тогда $obj->foo(@args) вызовет подпрограмму foo и передаст в качестве первого аргумента ссылку $obj сопровождаемый остальными аргументами (@args). Подпрограмма должна быть определена в пакете «Класс». Если в пакете "Class" нет подпрограммы foo, будет выполнен поиск списка других пакетов (взятых из массива @ISA в пакете "Class") и будет вызвана первая найденная подпрограмма foo.

9 голосов
/ 24 декабря 2008

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

5 голосов
/ 03 июня 2016

Эта функция сообщает объекту, на который ссылается 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 голосов
/ 27 августа 2016

Я дам здесь ответ, так как те, что здесь, не совсем мне понравились.

Функция благословения 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 не имеет ни "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};
})->();

Теперь у нас есть проблема: что, если мы хотим, чтобы животное исполняло звуки самостоятельно, а не мы печатали, каков их голос. То есть нам нужна функция executeSound, которая печатает собственный звук животного.

Один из способов сделать это - научить каждого отдельного животного тому, как это звучит. Это означает, что у каждого кота есть своя функция дублирования для выполнения звука.

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

Это плохо, потому что executeSound ставится как совершенно новый функциональный объект каждый раз, когда создается животное. 10000 животных означает 10000 звуков. Мы хотим иметь единственную функцию executeSound, которая используется всеми животными, которая ищет свой собственный звук и печатает его.

(() => {
    '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) будет эквивалентен javascript "это". В свою очередь, вы можете немного смоделировать модель прототипа JavaScript.

К сожалению, из-за этого невозможно (насколько я понимаю) создавать «новые классы» во время выполнения, так как каждый «класс» должен иметь свой собственный пакет, тогда как в javascript пакеты вообще не нужны, так как « Ключевое слово new создает анонимную хэш-карту, которую вы можете использовать в качестве пакета во время выполнения, к которому вы можете добавлять новые функции и удалять функции на лету.

Есть некоторые библиотеки Perl, создающие свои собственные способы преодоления этого ограничения в выразительности, такие как Moose.

Почему путаница? :

Из-за пакетов. Наша интуиция говорит нам привязать объект к хэш-карте, содержащей его прототип. Это позволяет нам создавать «пакеты» во время выполнения, как может JavaScript. Perl не обладает такой гибкостью (по крайней мере, не встроен, вы должны его придумать или получить из других модулей), и, в свою очередь, ваша выразительность во время выполнения будет затруднена. Называть его «благослови» не очень помогает ни тому, ни другому.

Что мы хотим сделать :

Нечто подобное, но привязка к прототипу карты рекурсивна и неявно связана с прототипом, вместо того, чтобы делать это явно.

Вот наивная попытка: проблема в том, что «вызов» не знает, «как он был вызван», так что это может быть универсальная perl-функция «objectInvokeMethod (object, method)», которая проверяет, является ли объект имеет метод, или его прототип имеет его, или его прототип имеет его, пока он не достигнет конца и найдет его или нет (прототип наследования). В 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'));
})->();

В любом случае, надеюсь, кто-нибудь найдет этот пост полезным.

1 голос
/ 05 мая 2015

I Следуя этой мысли, мы руководствуемся разработкой объектно-ориентированного Perl.

Благослови связь любой ссылки на структуру данных с классом. Учитывая, как Perl создает структуру наследования (в виде дерева), легко использовать преимущества объектной модели для создания объектов для композиции.

Для этой ассоциации, которую мы назвали объектом, для развития всегда следует помнить, что внутреннее состояние объекта и поведение классов разделены. И вы можете благословить / разрешить любой ссылке на данные использовать любое поведение пакета / класса. Так как в пакете можно понять «эмоциональное» состояние объекта.

0 голосов
/ 16 июня 2011

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

...