Вопрос

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

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

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

Это было полезно?

Решение

В целом, bless связывает объект с классом.

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

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

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

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

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

Редактировать:Видишь киксхэто хорошо ответ для получения немного более подробной информации.

Другие советы

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

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

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

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

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

Эта функция сообщает объекту, на который ссылается REF, что теперь он является объектом в пакете CLASSNAME или текущем пакете, если CLASSNAME опущено.Рекомендуется использовать форму bless с двумя аргументами.

Пример:

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

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

Функция bless в 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 года нет смысла записывать свойства в определенном порядке) и возвращает хэш-таблицу с этими свойствами, или, если вы забыли ввести ключевое слово new, она вернет весь глобальный контекст (например, window в браузере или global в 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, которая печатает собственный звук животного.

Один из способов сделать это - научить каждое отдельное Животное издавать свой звук.Это означает, что у каждой Кошки есть своя собственная дублирующая функция для выполнения звука.

# 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 помещается как совершенно новый функциональный объект каждый раз, когда создается animal.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
})();

Здесь параллель с 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 нет "этого", "класса" или "нового".включение объекта в пакет дает этому объекту ссылку на пакет, и когда он вызывает функции в пакете, их аргументы будут смещены на 1 слот, а первый аргумент ($_[0] или shift) будет эквивалентен "this" в javascript.В свою очередь, вы можете в некоторой степени имитировать прототипную модель JavaScript.

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

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

К чему эта путаница?:

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

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

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

Вот наивная попытка сделать это:проблема в том, что "call" не знает, "что это вызвало", поэтому с таким же успехом это может быть универсальная функция 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'));
})->();

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

Я следую этой мысли, чтобы направлять разработку объектно-ориентированного Perl.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top