在 Perl 中返回延迟计算的标量

发布于 2024-09-11 12:22:23 字数 953 浏览 7 评论 0原文

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

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

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

——巴克

I'm trying to add some functionality to our code base by using tied scalars.

We have a function which is specified to return scalars. I thought I could add some features to the system by tie-ing these scalars before returning them, but it looks like the FETCH method is called just before the return, which results in an untied scalar being returned.

Is there any way around this?

I really want to keep the subroutine's interface (returning scalars) intact if it's at all possible.

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";
}

Desired output:

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

I can get the desired output by returning a reference, and dereferencing on each access, but that ruins our established interface, and makes it more confusing for our users.

--Buck

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

国粹 2024-09-18 12:22:23

正如 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";
}

As DVK said, tie applies to containers, so isn't useful for returned values.

For that, you use overloading. An example (not all the possible overloaded operations are supplied; see 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";
}
终难愈 2024-09-18 12:22:23

正如其他答案中提到的,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";
};

联系和重载都可能变得复杂,因此如果有任何不清楚的地方,请通读所有文档。

As mentioned in other answers, tie applies to containers, and not to values, so there is no way to assign a tied variable to another variable and retain the tied properties.

Since assignment is out, you need to pass the container into the GetThing routine. You can do this by reference as follows:

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";
}

which produces the correct output.

However, if you want to retain the assignment, you will need to use overloading, which applies to values (actually to objects, but they themselves are values). Without more detail on your intended purpose it is hard to give a complete answer, but this will meet your stated requirements:

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";
};

Both ties and overloads can get complicated, so read through all of the documentation if anything is not clear.

渡你暖光 2024-09-18 12:22:23

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

  1. 绑定变量具有绑定 附加到变量本身,而不是其值。

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

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


第二,您不太清楚自己到底想要实现什么,因此很难提出如何实现您想要的目标。但是,假设根据您的描述,您想在子例程返回时调用某些方法(可能向其传递返回值),您可以这样做。

为此,您需要使用人们所说的 方面编程。在 Perl 中,政治上(和技术上)正确的方法是使用 Moose。

然而,你可以DIY它,基本上用包装方法替换原始方法。

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

用鸭子类型语言模拟静态类型的各个方面

First, the exact method of doing what you are proposing seems technically impossible:

  1. Tied variables have the tie attached to the variable itself, not to its value.

  2. In Perl, subroutine's return values are returned by value, meaning you take the value passed to return, access it (in you case, accessing the tied variable and calling FETCH in the process) - and then copy that value! Which means that what the caller gets is a scalar VALUE, not a scalar variable (tied or untied).

Your confusion, in short, seems to stem from mixing together variables (locations in program's symbol table) and values stored in those variables.


Second, you were somewhat unclear as to what exactly you are trying to achieve, so it's hard to propose how to achieve what you want. But assuming, based on your description, that you wanted to call some method upon subroutine's return (possibly passing it the return value), you CAN do that.

To do so, you need to employ what fancy people call aspect programming. The politically (and technically) correct way of doing it in Perl is by using Moose.

However, you can DIY it, by basically replacing the original method with a wrapper method.

The exact mechanics of both Moose and DIY approaches can be seen in the first two answers to the following SO question, so I won't copy/paste them here, hope you don't mind:

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

乖不如嘢 2024-09-18 12:22:23

如果您喜欢冒险,也可以使用 标量: :Defer 模块为标量变量提供了一种通用机制,可以延迟计算一次或每次访问时计算值。

If you're feeling adventurous, you could also use the Scalar::Defer module which provides a general-purpose mechanism for a scalar variable to compute a value lazily, either once or on each access.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文