возвращение лениво вычисляемого скаляра в perl - PullRequest
4 голосов
/ 29 июля 2010

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

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

Isесть ли способ обойти это?

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

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

Ответы [ 4 ]

4 голосов
/ 29 июля 2010

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

Для этого вы используете перегрузку. Пример (приведены не все возможные перегруженные операции; см. 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";
}
3 голосов
/ 30 июля 2010

Как уже упоминалось в других ответах, 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";
};

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

2 голосов
/ 29 июля 2010

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

  1. Связанные переменные имеют связь присоединяется к самой переменной, а не к ее значению.

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

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


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

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

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

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

Имитация аспектов статической типизации на языке с утиной типизацией

0 голосов
/ 30 июля 2010

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

...