Perl: Как создавать объекты на лету?

Моя цель - иметь возможность использовать $ 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;

12
задан Bill the Lizard 13 October 2017 в 12:15
поделиться

7 ответов

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 .

14
ответ дан 2 December 2019 в 05:39
поделиться

Более подход 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' } };
4
ответ дан 2 December 2019 в 05:39
поделиться

Попробуйте Hash :: AsObject из CPAN.

4
ответ дан 2 December 2019 в 05:39
поделиться

$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.

2
ответ дан 2 December 2019 в 05:39
поделиться

В какой бы функции вы ни создавали объект, вам необходимо вызвать 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);

Немного громоздко, но работает. (Возможно, вам даже не понадобится вторая стрелка, но я не уверен.)

1
ответ дан 2 December 2019 в 05:39
поделиться

Методы в 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 () - это то, как вы устанавливаете связь между ссылкой и пакетом.

0
ответ дан 2 December 2019 в 05:39
поделиться

В Perl это пишется немного по-другому:

my $obj = { foo => 1, hello => sub { return "world" } };
print $obj->{hello}() . $obj->{foo};

Но код неудобен. Предупреждение, которое вы видели о том, что ссылка не благословлена, говорит о том, что ваши объекты реализованы не так, как ожидает Perl. Оператор bless помечает объект пакетом, в котором следует начинать поиск его методов.

Расскажите нам, что вы хотите сделать с точки зрения вашей проблемной области, и мы сможем предложить предложения по более естественному подходу в Perl.

2
ответ дан 2 December 2019 в 05:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: