在perl中返回一个懒惰计算的标量

时间:2010-07-29 01:50:53

标签: perl fetch scalar tie

我正在尝试使用绑定标量为我们的代码库添加一些功能。

我们有一个指定返回标量的函数。我想我可以通过在返回之前绑定这些标量来为系统添加一些功能,但看起来就像在返回之前调用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

我可以通过返回引用来获得所需的输出,并在每次访问时取消引用,但这会破坏我们已建立的界面,并使我们的用户更加困惑。

- 降压

4 个答案:

答案 0 :(得分:4)

正如DVK所说,tie适用于容器,因此对返回的没有用。

为此,您使用重载。示例(并非提供所有可能的重载操作;请参阅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";
}

答案 1 :(得分: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";
};

关系和重载都会变得复杂,所以如果有什么不清楚,请仔细阅读所有文档。

答案 2 :(得分:2)

首先,执行您提出的建议的确切方法在技术上似乎是不可能的:

  1. 绑定的变量将tie附加到变量本身,而不是其值。

  2. 在Perl中,子程序的返回值按值返回,这意味着您将传递给return的值,访问它(在您的情况下,访问绑定变量并调用FETCH在此过程中) - 然后复制该值!这意味着调用者得到的是标量值,而不是标量变量(绑定或解除绑定)。

  3. 简而言之,您的困惑似乎源于将变量(程序符号表中的位置)和存储在这些变量中的值混合在一起。


    第二,你有点不清楚你究竟想要什么实现,所以很难提出如何实现你想要的。但是,根据您的描述,假设您想在子例程返回时调用某个方法(可能将其返回给返回值),您可以这样做。

    要做到这一点,你需要使用人们称之为aspect programming的幻想。在Perl中使用政治(和技术上)正确的方法是使用Moose。

    但是,您可以通过基本上用包装器方法替换原始方法来DIY它。

    Moose和DIY方法的确切机制可以在以下SO问题的前两个答案中看到,所以我不会在这里复制/粘贴它们,希望你不介意:

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

答案 3 :(得分:0)

如果您喜欢冒险,您还可以使用Scalar::Defer模块,该模块为标量变量提供通用机制,可以懒散地计算一个值,无论是一次还是每次访问。