Я розумію, що в Perl використовується ключове слово "благословити" всередині "нового" методу класу:
sub new {
my $self = bless { };
return $self;
}
Але що саме "благословляє" робити на це хеш-посилання?
Я розумію, що в Perl використовується ключове слово "благословити" всередині "нового" методу класу:
sub new {
my $self = bless { };
return $self;
}
Але що саме "благословляє" робити на це хеш-посилання?
Відповіді:
Взагалі bless
асоціює об’єкт із класом.
package MyClass;
my $object = { };
bless $object, "MyClass";
Тепер, коли ви викликаєте метод $object
, Perl дізнається, в якому пакеті шукати метод.
Якщо другий аргумент опущено, як у вашому прикладі, використовується поточний пакет / клас.
Для наочності ваш приклад може бути записаний так:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
EDIT: Дивіться хорошу відповідь kixx для отримання більш детальної інформації.
bless
пов'язує посилання з пакетом.
Неважливо, на що посилається, це може бути хеш (найчастіший випадок), масив (не настільки поширений), скаляр (зазвичай це вказує на об'єкт зсередини ), на регулярний вираз , підпрограма або TYPEGLOB (див. книгу « Орієнтований на об’єкт Perl: всебічний посібник з концепцій та методик програмування Даміана Конвей» для корисних прикладів) або навіть посилання на файл або каталог ручки (найменш поширений випадок).
Ефект bless
полягає в тому, що він дозволяє застосувати спеціальний синтаксис до блаженної посилання.
Наприклад, якщо блаженна посилання зберігається в $obj
(пов’язаному bless
з пакетом "Клас"), то $obj->foo(@args)
викличе підпрограму foo
і передасть в якості першого аргументу посилання, $obj
а потім решту аргументів ( @args
). Підпрограма повинна бути визначена в пакеті "Клас". Якщо foo
в пакеті "Клас" немає підпрограми, буде шуканий список інших пакетів (узятих із масиву @ISA
в пакеті "Клас"), і foo
буде викликано першу знайдену підпрограму .
Ця функція повідомляє об'єкту, на який посилається 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;
}
Я дам тут відповідь, оскільки ті, хто тут, не дуже натискали на мене.
Функція благословення 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.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
претензію. Я думаю, що моє занепокоєння в кінцевому рахунку зводилося до того, що він був значно менш інтуїтивним для маніпулювання / самоаналізу пакетної системи під час виконання, але поки що я не спромігся показати щось, що по суті не може зробити. Пакетна система, здається, підтримує всі інструменти, необхідні для додавання / видалення / перевірки / модифікації під час виконання.
Поряд з низкою хороших відповідей, що особливо вирізняється bless
aed-посиланням, є те, що 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
), в пакеті (або ієрархії) шукається суб з цим ім'ям, об'єкт передається як перший аргумент тощо.
Я, дотримуючись цієї думки, керував розробкою об'єктно-орієнтованого Perl.
Благословіть асоціювання будь-яких посилань на структуру даних з класом. З огляду на те, як Perl створює структуру спадкування (у вигляді дерева), легко скористатися об'єктною моделлю для створення об'єктів для композиції.
Для цього об'єднання ми назвали об'єкт, щоб розвиватися завжди маючи на увазі, що внутрішній стан поведінки об'єкта та класу є розділеними. І ви можете благословити / дозволити будь-які посилання на дані використовувати будь-які поведінки пакунків / класів. Оскільки в пакеті можна зрозуміти "емоційний" стан об'єкта.
Наприклад, якщо ви можете бути впевнені, що будь-який об’єкт 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 вилучає посилання, яке було передано в якості першого аргументу, а потім оператори друку отримують доступ до різних записів блаженного хеша.