Моя цель - иметь возможность использовать $ obj
следующим образом :
print $obj->hello() . $obj->{foo};
И я хотел бы создать встроенный объект, возможно, используя что-то , например this:
my $obj = (
foo => 1,
hello => sub { return 'world' }
);
, но когда я пытаюсь использовать $ obj
в качестве объекта, я получаю ошибку говоря, что $ obj не был благословлен . Есть ли какой-то базовый класс (например, stdClass
в PHP), который я могу использовать, чтобы благословить хеш, чтобы я мог использовать его как объект?
Для тех, кто знает JavaScript, я пытаюсь сделать следующее , но в Perl:
# JS CODE BELOW
var obj = { foo: 1, hello: function () { return 'world' } };
echo obj.hello() + obj.foo;
Perl потребуется небольшая помощь для этого.Потому что он не считает ссылки на код, хранящиеся в хэшах, «методами». Методы реализованы как записи в таблице символов пакета. Perl более ориентирован на классы , чем JavaScript, который с гордостью заявляет, что он более объектно-ориентированный (на индивидуальных объектах).
Чтобы реализовать эту функциональность, вам нужно будет создать класс, который будет отображать ссылки таким образом. Способом обхода методов в таблице символов является метод AUTOLOAD
. Если пакет содержит подпрограмму AUTOLOAD
, при вызове благословенного объекта, который Perl не может найти в цепочке наследования, он вызовет AUTOLOAD
и установит область действия пакета ( наша
) переменная $ AUTOLOAD
будет содержать полное имя функции.
Мы получаем имя вызываемого метода, получая последний узел (после последнего «::») полностью определенного под-имени. Мы смотрим, есть ли в этом месте ссылка на код, и если она есть, мы можем вернуть ее.
package AutoObject;
use strict;
use warnings;
use Carp;
use Params::Util qw<_CODE>;
our $AUTOLOAD;
sub AUTOLOAD {
my $method_name = substr( $AUTOLOAD, index( $AUTOLOAD, '::' ) + 2 );
my ( $self ) = @_;
my $meth = _CODE( $self->{$method_name} );
unless ( $meth ) {
Carp::croak( "object does not support method='$method_name'!" );
}
goto &$meth;
}
1;
Затем вы должны добавить объект к этому классу:
package main;
my $obj
= bless { foo => 1
, hello => sub { return 'world' }
}, 'AutoObject';
print $obj->hello();
Обычно в AUTOLOAD
sub I поведение «цементирует». То есть я создаю записи в таблице символов пакета, чтобы избежать AUTOLOAD
в следующий раз. Но это обычно для разумно определенного поведения класса.
Я также разработал QuickClass
, который создает пакет для каждого объявленного объекта, но содержит много споров о таблице символов, что в наши дни, вероятно, лучше сделать с помощью Class :: MOP
.
Учитывая предложение Эрика Строма, вы можете добавить следующий код в пакет AutoObject. Подложка import
будет вызываться каждый раз, когда кто-то использует
-d AutoObject
(с параметром 'object'
).
# Definition:
sub object ($) { return bless $_[0], __PACKAGE__; };
sub import { # gets called when Perl reads 'use AutoObject;'
shift; # my name
return unless $_[0] eq 'object'; # object is it's only export
use Symbol;
*{ Symbol::qualify_to_reference( 'object', scalar caller()) }
= \&object
;
}
А затем, когда вы хотите создать «литерал объекта», вы можете просто сделать:
use AutoObject qw<object>;
И выражение будет:
object { foo => 1, hello => sub { return 'world' } };
Вы даже можете сделать:
object { name => 'World'
, hello => sub { return "Hello, $_[0]->{name}"; }
}->hello()
;
И у вас есть «литерал объекта» выражение. Возможно, модуль лучше называть Object :: Literal
.
Более подход Perlish состоит в том, чтобы создать отдельное пространство имен для желаемых методов вашего объекта и благословить
объект, чтобы сделать эти методы доступными для вашего объекта. Код для этого все еще может быть довольно кратким.
my $obj = bless { foo => 1 }, "bar";
sub bar::hello { return 'world' };
Как предлагает gbacon, если вы хотите написать $ obj -> {hello} -> ()
вместо $ obj-> hello ()
, вы можете пропустить Благослови операцию.
my $obj = { foo => 1, hello => sub { return 'world' } };
$obj
будет скаляром, поэтому все, что вы назначите ему, также должно быть скаляром. Вы можете сказать либо
my %obj = ( foo => 1, hello => sub { return 'world' });
либо
my $obj = { foo => 1, hello => sub { return 'world' }};
Последнее, с фигурными скобками, создает хэш-ссылку (которая является скаляром, поэтому может быть помещена в $obj
). Чтобы перейти к содержимому внутри хэш-ссылки, необходимо использовать оператор стрелки. Что-то вроде $obj->{foo}
или &{$obj->{hello}}
.
Если только вам не нужно иметь списки хэшей или что-то в этом роде, обычно лучше использовать первый метод.
В любом случае, вы не сможете сказать $obj->hello()
. Perl использует этот синтаксис для своей собственной разновидности ООП, в которой функция hello
находится в отдельном пакете, в который была благословлена
ваша ссылка. Например:
package example;
sub new {} { my $result = {}; return bless $result, 'example' }
sub hello { return 'world' }
package main;
my $obj = example->new();
Как вы видите, методы, которые вы можете вызывать, уже определены, и добавить новые не так уж тривиально. Существуют магические методы, которые можно использовать для этого, но на самом деле оно того не стоит. &{$obj{hello}}
(или &{$obj->{hello}}
для справки) - это меньше усилий, чем пытаться заставить Perl работать как Javascript.
В какой бы функции вы ни создавали объект, вам необходимо вызвать bless
для вашего объекта, чтобы разрешить вызов метода.
Например:
package MyClass;
sub new
{
my $obj = {
foo => 1
};
return bless($obj, "MyClass");
}
sub hello
{
my $self = shift;
# Do stuff, including shifting off other arguments if needed
}
package main;
my $obj = MyClass::new();
print "Foo: " . $obj->{foo} . "\n";
$obj->hello();
РЕДАКТИРОВАТЬ: Если вы хотите иметь возможность использовать ссылки на подпрограммы для обеспечения динамической функциональности для ваших объектов ...
Во-первых, вы можете создать ссылку на свой код следующим образом (в этом примере конструктора хэша ):
my $obj = {
foo => 1,
hello => sub { print "Hello\n"; },
}
Вы можете вызвать его так:
my $obj = MyClass::new(); # or whatever
$obj->{hello}->(@myArguments);
Немного громоздко, но работает. (Возможно, вам даже не понадобится вторая стрелка, но я не уверен.)
Методы в Perl - это не свойства объекта, как в Python. Методы - это простые обычные функции-функции в пакете , связанные с объектом. Обычные функции, принимающие дополнительный аргумент для ссылки на себя.
Вы не можете иметь динамически создаваемые функции как методы.
Вот цитата из perldoc perlobj:
1. An object is simply a reference that happens to know which class it
belongs to.
2. A class is simply a package that happens to provide methods to deal
with object references.
3. A method is simply a subroutine that expects an object reference
(or a package name, for class methods) as the first argument.
О, и bless () - это то, как вы устанавливаете связь между ссылкой и пакетом.
В Perl это пишется немного по-другому:
my $obj = { foo => 1, hello => sub { return "world" } };
print $obj->{hello}() . $obj->{foo};
Но код неудобен. Предупреждение, которое вы видели о том, что ссылка не благословлена, говорит о том, что ваши объекты реализованы не так, как ожидает Perl. Оператор bless
помечает объект пакетом, в котором следует начинать поиск его методов.
Расскажите нам, что вы хотите сделать с точки зрения вашей проблемной области, и мы сможем предложить предложения по более естественному подходу в Perl.