2010-07-29 4 views
4

Я пытаюсь добавить некоторые функциональные возможности в нашу базу кода, используя привязанные скаляры.возвращает лениво-вычисленный скаляр, в perl

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

Есть ли способ обойти это?

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

use strict; 
use warnings; 
main(); 

sub GetThing{ 
    my $thing; 
    tie $thing, 'mything', @_; 
    return $thing; 
} 

sub main { 
    my %m; 
    $m{pre} = GetThing('Fred'); 
    print "1\n"; 
    print $m{pre}; 
    print "2\n"; 
    print $m{pre}; 
    print "3\n"; 
} 


package mything; 
require Tie::Scalar; 

my @ISA = qw(Tie::StdScalar); 

sub TIESCALAR { 
    my $class = shift; 
    bless { 
     name => shift || 'noname', 
    }, $class; 
} 

sub FETCH { 
    my $self = shift; 
    print "ACCESS ALERT!\n"; 
    return " NAME: '$self->{name}'\n"; 
} 

Желаемый результат:

1 
ACCESS ALERT! 
    NAME: 'Fred' 
2 
ACCESS ALERT! 
    NAME: 'Fred' 
3 

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

--Buck

+3

«Код доступен по запросу» - запрошен. –

ответ

4

Как сказал ДВК, галстук относится к контейнерам, так что не является полезным для возвращаемых значений .

Для этого вы используете перегрузку. Пример (не все возможные перегруженные операции поставляются, см http://perldoc.perl.org/overload.html#Minimal-set-of-overloaded-operations):

use strict; 
use warnings; 
main(); 

sub GetThing{ 
    my $thing; 
    $thing = "mything"->new(@_); 
    return $thing; 
} 

sub main { 
    my %m; 
    $m{pre} = GetThing('Fred'); 
    print "1\n"; 
    print $m{pre}; 
    print "2\n"; 
    print $m{pre}; 
    print "3\n"; 
} 


package mything; 
use overload 'fallback' => 1, '""' => 'FETCH'; 

sub new { 
    my $class = shift; 
    bless { 
     name => shift || 'noname', 
    }, $class; 
} 

sub FETCH { 
    my $self = shift; 
    print "ACCESS ALERT!\n"; 
    return " NAME: '$self->{name}'\n"; 
} 
+0

Не могли бы вы рассказать о том, как в этой ситуации будет применяться перегрузка? Спасибо! – DVK

+0

да пожалуйста уточните. Я думал, что tie() был правильным способом перегрузить FETCH на скаляр. – bukzor

+0

@buzkor: по скалярной переменной, а не по скалярному значению. – ysth

2

Первый, точный метод делать то, что вы предлагаете, кажется технически невозможно:

  1. вязаные переменные имеют tie прикреплены к самой переменной, а не к его значение.

  2. В Perl, возвращаемые значения SUBROUTINE будут возвращены по значению означает, что вы принять значение, передаваемое return, доступ к нему (в вас случае, доступ к привязанной переменной и вызова FETCH в процессе) - , а затем скопировать это значение! Это означает, что то, что получает вызывающий объект, является скалярным значением VALUE, а не скалярной переменной (связанной или развязанной).

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


Второй, вы были несколько неясно, что именно вы пытаетесь достичь , так что трудно предложить, как добиться того, что вы хотите. Но предположив, что на основе вашего описания вы хотели бы вызвать некоторый метод при возврате подпрограммы (возможно, передав ему возвращаемое значение), вы МОЖЕТЕ это сделать.

Для этого вам необходимо использовать то, что причудливые люди называют aspect programming. Политически (и технически) правильный способ сделать это в Perl - это использовать Moose.

Однако вы можете сделать это самостоятельно, заменив исходный метод на метод обертки.

точной механики обоих лосей и DIY подходов можно увидеть в первых двух ответов на следующий вопрос SO, поэтому я не буду копировать/вставить их здесь, надеюсь, вы не против:

Simulating aspects of static-typing in a duck-typed language

+0

** Caveat **: я никогда не использовал привязанные скаляры, поэтому самая лучшая часть ответа - это всего лишь результат моей аналитической документации и, следовательно, может быть в стороне, как и все, что основано на теории ... я с удовольствием удалю ее когда/если кто-то с большим ключом приходит вместе с лучшим. – DVK

+0

линия между переменными и значениями размывается, когда возвращаемая подпрограмма объявляется как 'lvalue' (поскольку в этом случае работают« FETCH »и« STORE »). вы также можете взять ссылку на возвращенный псевдоним, который сохраняет связанное действие, когда оно разыменовано. –

+2

@ Eric Strom: если вы собираетесь на эту проблему, можете сделать так, чтобы процедура возвращала ссылку (или была передана одна!) В первую очередь – ysth

3

Как уже упоминалось в других ответах, tie относится к контейнерам, а не к значениям, так что нет никакого способа, чтобы назначить привязанную переменную другую переменную и сохранить связанные свойства.

Поскольку назначение отсутствует, вам необходимо передать контейнер в рутину GetThing. Вы можете сделать это по ссылке:

use strict; 
use warnings; 
main(); 

sub GetThing{ 
    tie ${$_[1]}, 'mything', $_[0]; 
} 

sub main { 
    my %m; 
    GetThing('Fred' => \$m{pre}); 
    print "1\n"; 
    print $m{pre}; 
    print "2\n"; 
    print $m{pre}; 
    print "3\n"; 
} 


package mything; 
require Tie::Scalar; 

my @ISA = qw(Tie::StdScalar); 

sub TIESCALAR { 
    my $class = shift; 
    bless { 
     name => shift || 'noname', 
    }, $class; 
} 

sub FETCH { 
    my $self = shift; 
    print "ACCESS ALERT!\n"; 
    return " NAME: '$self->{name}'\n"; 
} 

, который производит правильный выход.

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

use strict; 
use warnings; 
main(); 

sub GetThing{ 
    return mything->new(shift); 
} 

sub main { 
    my %m; 
    $m{pre} = GetThing('Fred'); 
    print "1\n"; 
    print $m{pre}; 
    print "2\n"; 
    print $m{pre}; 
    print "3\n"; 
} 


package mything; 

sub new { 
    my $class = shift; 
    bless { 
     name => shift || 'noname', 
    }, $class; 
} 

use overload '""' => sub { # '""' means to overload stringification 
    my $self = shift; 
    print "ACCESS ALERT!\n"; 
    return " NAME: '$self->{name}'\n"; 
}; 

Оба связи и перегрузки может усложниться, поэтому прочитать всю документацию, если что-то не Чисто.

+0

похоже, что это может сделать трюк. Цель состоит в том, чтобы вычислить мои строки по требованию, ленивым способом, а также сделать некоторые дорогостоящие операции над значениями по мере необходимости. Наши пользователи могут создавать множество значений, но в конечном итоге использовать только несколько. Пользователи не являются экспертами, чтобы сказать наименее, поэтому, если мы можем сделать его похожим на строку плана, это лучше всего. Я попробую и вернусь к тебе через минуту. – bukzor

+0

работает как очарование! Я изменил название, чтобы лучше отразить мою проблему. Я бы удалил первую половину вашего ответа. – bukzor

+0

@bukzor => вы также можете взглянуть на мой модуль 'List :: Gen' на CPAN, который содержит довольно надежную реализацию ленивого списка –

0

Если вы чувствуете себя авантюрно, вы также можете использовать модуль Scalar::Defer, который обеспечивает механизм общего назначения для скалярной переменной для вычисления значения лениво, один раз или при каждом доступе.

+0

Это кажется лучше, чем писать мои собственные. Вы использовали его? Непонятно из документа: возвращает ли $ x/2 другое отложенное значение или простое значение? – bukzor

+0

Он вернет равное значение. –