我正在尝试使用绑定标量为我们的代码库添加一些功能。
我们有一个指定返回标量的函数。我想我可以通过在返回之前绑定这些标量来为系统添加一些功能,但看起来就像在返回之前调用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
我可以通过返回引用来获得所需的输出,并在每次访问时取消引用,但这会破坏我们已建立的界面,并使我们的用户更加困惑。
- 降压
答案 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)
首先,执行您提出的建议的确切方法在技术上似乎是不可能的:
绑定的变量将tie附加到变量本身,而不是其值。
在Perl中,子程序的返回值按值返回,这意味着您将传递给return
的值,访问它(在您的情况下,访问绑定变量并调用FETCH
在此过程中) - 然后复制该值!这意味着调用者得到的是标量值,而不是标量变量(绑定或解除绑定)。
简而言之,您的困惑似乎源于将变量(程序符号表中的位置)和存储在这些变量中的值混合在一起。
第二,你有点不清楚你究竟想要什么实现,所以很难提出如何实现你想要的。但是,根据您的描述,假设您想在子例程返回时调用某个方法(可能将其返回给返回值),您可以这样做。
要做到这一点,你需要使用人们称之为aspect programming的幻想。在Perl中使用政治(和技术上)正确的方法是使用Moose。
但是,您可以通过基本上用包装器方法替换原始方法来DIY它。
Moose和DIY方法的确切机制可以在以下SO问题的前两个答案中看到,所以我不会在这里复制/粘贴它们,希望你不介意:
Simulating aspects of static-typing in a duck-typed language
答案 3 :(得分:0)
如果您喜欢冒险,您还可以使用Scalar::Defer模块,该模块为标量变量提供通用机制,可以懒散地计算一个值,无论是一次还是每次访问。