Что именно делает Perl "благословить"? - программирование

Что именно делает Perl "благословить"?

Насколько я понимаю, в Perl внутри класса "новый" используется ключевое слово "благо":

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

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

4b9b3361

Ответ 1

В общем случае bless связывает объект с классом.

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

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

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

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

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

EDIT: см. kixx answer для более подробной информации.

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

Я дам ответ здесь, так как те, которые здесь не совсем щелкнули для меня.

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

# 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 имеет свою собственную дублируемую функцию для выполнения 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 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 name is Sam?
    var correct = new Person; // person 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 "this". В свою очередь, вы можете несколько имитировать модель прототипа JavaScript.

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

Существуют некоторые библиотеки 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'));
})->();

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

Ответ 7

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