Подтвердить что ты не робот

Когда следует использовать атрибуты подпрограммы?

Я не проверяю атрибуты подпрограммы Perl вообще.

Я никогда не видел их в реальном коде и perldoc perlsub и perldoc attributes не могут ответить на мои вопросы:

  • Какие атрибуты полезны для?
  • Что они приносят в таблицу, которая еще не присутствует в лучших практиках Perl?
  • Существуют ли какие-либо CPAN-модули (известные или другие), которые используют атрибуты?

Было бы здорово, если бы кто-то мог собрать подробный пример использования атрибутов так, как они должны быть.


Для тех, кто так же невежественен, как и я, атрибуты являются параметрами после двоеточия в примерах attributes SYNOPSIS:

sub foo : method ;
my ($x,@y,%z) : Bent = 1;
my $s = sub : method { ... };

use attributes ();  # optional, to get subroutine declarations
my @attrlist = attributes::get(\&foo);

use attributes 'get'; # import the attributes::get subroutine
my @attrlist = get \&foo;
4b9b3361

Ответ 1

Атрибуты позволяют вам комментировать переменные для выполнения автоматической магии за кулисами. Аналогичная концепция аннотация java. Вот небольшой пример, который может помочь. Он использует Attribute::Handlers для создания атрибутов loud.

use Attribute::Handlers;

sub UNIVERSAL::loud : ATTR(CODE) {
    my ( $pkg, $sym, $code ) = @_;
    no warnings 'redefine';
    *{$sym} = sub {
        return uc $code->(@_);
    };
}

sub foo : loud {
    return "this is $_[0]";
}

say foo("a spoon");
say foo("a fork");

Всякий раз, когда sub объявляется атрибутом loud, обратный вызов UNIVERSAL::loud вызывает выставление метаинформации на суб. Я переопределил функцию, чтобы на самом деле вызывать анонимный суб, который, в свою очередь, вызывает исходный суб и передает его на uc

Выводится:

THIS IS A SPOON
THIS IS A FORK

Теперь рассмотрим пример переменной из SYNOPSIS:

my ($x,@y,%z) : Bent = 1;

Разбивая это на небольшую инструкцию perl без учета атрибутов, мы имеем

my $x : Bent
$x = 1;

my @y : Bent
@y = 1;

my %Z : Bent
%z = 1;

Теперь мы можем видеть, что каждая переменная была сжата аннотацией Бента кратким образом, а также присваивает всем переменным значение 1. Вот, может быть, более интересный пример:

use Attribute::Handlers;
use Tie::Toggle;

sub UNIVERSAL::Toggle : ATTR(SCALAR) {
    my ($package, $symbol, $referent, $attr, $data, $phase) = @_;
    my @data = ref $data eq 'ARRAY' ? @$data : $data;
    tie $$referent, 'Tie::Toggle', @data;
}

my $x : Toggle;

say "x is ", $x;
say "x is ", $x;
say "x is ", $x;

Какие выходы:

x is 
x is 1
x is 

Вы можете использовать это для ведения журнала, создания тестовых аннотаций, добавления данных типа к переменным, синтаксического сахара, создания ролевой композиции лося и многих других интересных вещей.

Также см. этот вопрос: Как работают атрибуты метода Perl?.

Ответ 2

  • Какие атрибуты полезны для?

Это способ передать некоторую дополнительную информацию (атрибут) о переменной или подпрограмме.

Вы можете поймать эту информацию (атрибут) в виде строки (в COMPILE TIME!) и обрабатывать его, как вам нравится. Вы можете создать дополнительный код, изменять тиски.... Это зависит от вас.

  • Что они приносят в таблицу, которая еще не присутствует в лучших практиках Perl?

Иногда это облегчает жизнь. См. Пример ниже.

Некоторые используют его. Сделайте: find. -name *.p [ml] | xargs grep 'использовать атрибуты; на вашем пути установки perl для просмотра пакетов с использованием атрибутов. Catalyst широко использует атрибуты для обработки запросов на основе заданного пути.

Пример:

Предположим, что вы выполняете подпрограммы в определенном порядке. И вы хотите сказать подпрограммой, когда она должна выполняться (по номеру запуска RUNNR). Использование атрибутов реализация может быть:

#!/usr/bin/env perl

use strict;
use warnings;

use Runner;     # immplements the attribute handling

# some subroutines to be scheduled :
# attibutes automatically filling @$Runner::schedule 
sub func_a : RUNNR(2) {return "You called func_a !"};
sub func_b : RUNNR(1) {return "You called func_b !"};
sub func_c : RUNNR(3) {return "You called func_c !"};

# run the subroutines according to the their RUNNR
sub run {
    # @$Runner::schedule holds the subroutine refs according
    # to their RUNNR
    foreach my $func (@$Runner::schedule) {
       if ( defined $func ) {
         print "Running : $func --> ", $func->(), "\n";
       }
    }
}

print "Starting ...\n\n";
run();
print "\nDone !\n";

Обработка атрибутов выполняется в пакете Runner с помощью MODIFY_CODE_ATTRIBUTES крюк.

package Runner;

use strict;
use warnings;

use attributes;

BEGIN {
    use Exporter ();                                                                 
    our (@ISA, @EXPORT);       

    @ISA         = qw(Exporter);                 
    @EXPORT      = qw(&MODIFY_CODE_ATTRIBUTES);    # needed for use attributes;    
}

# we have subroutines with attributes : <type> is CODE in MODIFY_<type>_ATTRIBUTES
# MODIFY_CODE_ATTRIBUTES is executed at COMPILE TIME ! try perl -c <prog_name> to prove it :-)

sub MODIFY_CODE_ATTRIBUTES {
    # for each subroutine of a package we get
    # the code ref to it and the attribute(s) as string
    my ($pckg, $code_ref, @attr) = @_;

    # whatever you like to do with the attributes of the sub ... do it
    foreach my $attr (@attr) {
        # here we parse the attribute string(s), extract the number and 
        # save the code ref of the subroutine
        # into $Runner::schedule array ref according to the given number
        # that is how we 'compile' the RUNNR of subroutines into 
        # a schedule
        if ( $attr =~ /^RUNNR\((\d+)\)$/ ) {    
            $Runner::schedule->[$1] = $code_ref;     
        }
    }
    return(); # ERROR if returning a non empty list
}

1;

Выход будет:

Starting ...

Running : CODE(0x129c288) --> You called func_b !
Running : CODE(0x129c2b8) --> You called func_a !
Running : CODE(0x12ed460) --> You called func_c !

Done !

Если вы действительно хотите понять, какие атрибуты и когда что происходит, вы должны "perldoc атрибуты", читать его шаг за шагом и играть с ним. Интерфейс является громоздким, но в принципе вы забираете время компиляции и обрабатываете предоставленная информация.

Ответ 3

При создании атрибутов можно использовать атрибуты tie. См. Глупый модуль Tie::Hash::Cannabinol, который позволяет:

use Tie::Hash::Cannabinol;

my %hash;
tie %hash, 'Tie::Hash::Cannabinol';

## or ##

my %hash : Stoned;

Изменить: при более глубоком рассмотрении T:: H:: C (hehe) использует Attribute::Handlers (как уже говорит JRideout ответ) так что, возможно, это место для поиска.