简体   繁体   English

检查子例程是否在Perl中用作左值或右值

[英]Check if a subroutine is being used as an lvalue or an rvalue in Perl

I'm writing some code where I am using a subroutine as both an lvalue and an rvalue to read and write database values. 我正在编写一些代码,其中我使用子例程作为左值和右值来读取和写入数据库值。 The problem is, I want it to react differently based on whether it is being used as an lvalue or an rvalue. 问题是,我希望它根据它是用作左值还是右值来做出不同的反应。

I want the subroutine to write to the database when it is used as an lvalue, and read from the database when it is used as an rvalue. 我希望子程序在用作左值时写入数据库,并在数据库用作右值时从数据库中读取。

Example: 例:

# Write some data
$database->record_name($subscript) = $value;

# Read some data
my $value = $database->record_name($subscript);

The only way I can think of the make this work is to find a way for the subroutine to recognize whether it is being used as an lvalue or an rvalue and react differently for each case. 我能想到的唯一方法就是找到一种方法让子程序识别它是用作左值还是左值,并对每种情况做出不同的反应。

Is there a way to do this? 有没有办法做到这一点?

Deciding how to behave on whether it was called as an lvalue or not is a bad idea since foo(record_name(...)) would call it as an lvalue. 决定如何调用它是否被称为左值是一个坏主意,因为foo(record_name(...))会将其称为左值。

Instead, you should decide how to behave on whether it is used as an lvalue or not. 相反,您应该决定如何将其用作左值。

You can do that by returning a magical value . 你可以通过返回一个神奇的值来做到这一点。

use Variable::Magic qw( cast wizard );

my $wiz = wizard(
   data => sub { shift; \@_ },
   get => sub { my ($ref, $args) = @_; $$ref = get_record_name(@$args); },
   set => sub { my ($ref, $args) = @_; set_record_name(@$args, $$ref); },
);

sub record_name :lvalue {
   cast(my $rv, $wiz, @_);
   return $rv;
}

A little test: 一点测试:

use Data::Dumper;

sub get_record_name { print("get: @_\n"); return "val"; }
sub set_record_name { print("set: @_\n"); }

my $x = record_name("abc", "def");        # Called as rvalue

record_name("abc", "def") = "xyz";        # Called as lvalue. Used as lvalue.

my $y_ref = \record_name("abc", "def");   # Called as lvalue.
my $y = $$y_ref;                          #   Used as rvalue.
$$y_ref = "xyz";                          #   Used as lvalue.

Output: 输出:

get: abc def
set: abc def xyz
get: abc def
set: abc def xyz

After seeing this, you've surely learned that you should abandon the idea of using an lvalue sub. 看到这个之后,你肯定知道你应该放弃使用左值子的想法。 It's possible to hide all that complexity (such as by using sentinel ), but the complexity remains. 隐藏所有复杂性(例如使用sentinel )是可能的,但复杂性仍然存在。 The fanciness is not worth all the complexity. 这种复杂性并不值得。 Use separate setters and getters or use an accessor whose role is based on the number of parameters passed to it ( $s=acc(); vs acc($s) ) instead. 使用单独的setter和getter或使用一个访问器,其角色基于传递给它的参数数量( $s=acc(); vs acc($s) )。

For this situation you might like to try my Sentinel module. 对于这种情况,您可能想尝试我的Sentinel模块。

It provides a function you can use in the accessor, to turn it into a more get/set style approach. 它提供了一个可以在访问器中使用的功能,将其转换为更加获取/设置的方式。 Eg you could 你可以

use Sentinel qw( sentinel );

sub get_record_name { ... }
sub set_record_name { ... }

sub record_name
{
   sentinel get => \&get_record_name,
            set => \&set_record_name,
            obj => shift;
}

At this point, the following pairs of lines of code are equivalent 此时,以下成对的代码行是等效的

$name = $record->record_name;
$name = $record->get_record_name;

$record->record_name = $new_name;
$record->set_record_name( $new_name );

Of course, if you're not needing to provide the specific get_ and set_ prefixed versions of the methods as well, you could inline them as closures. 当然,如果您不需要提供方法的特定get_set_前缀版本,则可以将它们内联为闭包。

See the module docs also for further ideas. 有关更多想法,请参阅模块文档。

In my opinion, lvalue subroutines in Perl were a dumb idea. 在我看来,Perl中的左值子程序是一个愚蠢的想法。 Just support ->record_name($subscript, $value) as a setter and ->record_name($subscript) as a getter. 只需支持->record_name($subscript, $value)作为setter,并将->record_name($subscript)作为getter。

That said, you can use the Want module, like this 也就是说,您可以像这样使用Want模块

use Want;

sub record_name:lvalue {
    if ( want('LVALUE') ) {
        ...
    }
    else {
        ...
    }
}

though that will also treat this as an LVALUE: 虽然这也将它视为LVALUE:

foo( $database->record_name($subscript) );

If you want only assignment statements to be treated specially, use want('ASSIGN') instead. 如果您只想要特别处理赋值语句,请使用want('ASSIGN')代替。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM