Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Perl中隐藏用户的tie调用_Perl_Oop_Tie - Fatal编程技术网

在Perl中隐藏用户的tie调用

在Perl中隐藏用户的tie调用,perl,oop,tie,Perl,Oop,Tie,我如何隐藏对用户的“tie”调用,以便调用访问器将隐式地为用户执行该操作 我想这样做,因为我有一个用户可以访问的数据结构,但是存储在这个结构中的值可以在用户不知情的情况下修改 如果数据结构中的某个属性发生了更改,我希望引用该属性的任何变量也会被修改,以便用户始终使用新数据。因为用户总是想要新的数据,所以如果用户甚至不需要知道它正在发生,那么它就更简单、更直观 这就是我目前所拥有的。。。但它似乎不起作用,输出为: hello hello 我想要的是: hello goodbye 代码: ret

我如何隐藏对用户的“tie”调用,以便调用访问器将隐式地为用户执行该操作

我想这样做,因为我有一个用户可以访问的数据结构,但是存储在这个结构中的值可以在用户不知情的情况下修改

如果数据结构中的某个属性发生了更改,我希望引用该属性的任何变量也会被修改,以便用户始终使用新数据。因为用户总是想要新的数据,所以如果用户甚至不需要知道它正在发生,那么它就更简单、更直观

这就是我目前所拥有的。。。但它似乎不起作用,输出为:

hello
hello
我想要的是:

hello
goodbye
代码:


return$text
只返回变量的值,而不是变量本身。您可以返回对它的引用,但是:

sub text {
    my ($self) = @_;
    tie my $text, 'FileText', $self;
    return \$text;
}
然后,您必须使用
$$text
取消对它的引用:

my $file = 'File'->new('_text' => 'hello');

my $text = $file->text();
say $$text;

$file->_text('goodbye');
say $$text;

我不建议这样做。您正在引入“远距离操作”,这会导致一些很难捕获的bug。用户认为他们得到了一个字符串。词法字符串只能通过直接和明显地改变它来改变。它必须在适当的地方被修改,或者明显地被传递到一个函数或者附加到某个东西的引用中

my $text = $file->text;
say $text;  # let's say it's 'foo'

...do some stuff...
$file->text('bar');
...do some more stuff...

# I should be able to safely assume it will still be 'foo'
say $text;
这段代码很容易理解,因为所有可能影响
$text
的东西都可以立即看到。这就是词汇上下文的意义所在,它隔离了可以改变变量的内容

通过返回一个随时可能改变的东西,你已经悄悄地打破了这个假设。没有迹象表明用户的假设已经被打破。当他们去打印
$text
并获得
时,不明显的是什么改变了
$text
。整个程序中的任何内容都可能更改
$text
。这一小块代码现在变得无限复杂

另一种方法是:Perl中的标量变量有一个定义的接口。该界面的一部分说明了如何更改它们。您正在破坏此界面并对用户撒谎。这就是重载/绑定变量通常被滥用的方式

无论您试图解决什么问题,您都是通过添加更多问题来解决的,通过使代码更复杂、更难理解来解决的。我会退后一步,问你想用搭售来解决什么问题

我要做的是只返回一个标量引用。这会提醒用户,可以随时将其从下方更改。没有魔法可以掩盖一条非常重要的信息

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

{
    package File;
    use Moose;

    has 'text_ref' => (
        is              => 'rw',
        isa             => 'Ref',
        default         => sub {
            return \("");
        }
    );

    sub BUILDARGS {
        my $class = shift;
        my %args  = @_;

        # "Cast" a scalar to a scalar ref.
        if( defined $args{text} ) {
            $args{text_ref} = \(delete $args{text});
        }

        return \%args;
    }

    sub text {
        my $self = shift;

        if( @_ ) {
            # Change the existing text object.
            ${$self->text_ref} = shift;
            return;
        }
        else {
            return $self->text_ref;
        }
    }
}

my $file = 'File'->new('text' => 'hello');

my $text = $file->text();
say $$text;

$file->text('goodbye');
say $$text;
也就是说,这是你如何做你想做的

我建议不要打领带。它非常慢,比方法调用慢得多,有缺陷且古怪。它的一个怪癖是绑定的性质附加到变量本身,而不是引用的数据。这意味着您不能返回绑定变量

相反,我建议使用重载对象来存储更改的文本

{
    package ChangingText;

    # Moose wants class types to be in a .pm file.  We have to explciitly
    # tell it this is a class type.
    use Moose::Util::TypeConstraints qw(class_type);
    class_type('ChangingText');

    use overload
      '""' => sub {
          my $self = shift;
          return $$self;
      },
      fallback => 1;

    sub new {
        my $class = shift;
        my $text = shift;
        return bless \$text, $class;
    }

    sub set_text {
        my $self = shift;
        my $new_text = shift;

        $$self = $new_text;

        return;
    }
}
重载对象有自己的警告,主要是由于代码要求字符串编写类似于
if!ref$arg
,但它们比深结bug更容易处理

要使其透明,请将ChangingText对象存储在File对象中,然后在其周围放置一个手工制作的
text
accessor来处理普通字符串。访问器确保重用相同的ChangingText对象

{
    package File;
    use Moose;

    has 'text_obj' => (
        is              => 'rw',
        isa             => 'ChangingText',
        default         => sub {
            return ChangingText->new;
        }
    );

    sub BUILDARGS {
        my $class = shift;
        my %args  = @_;

        # "Cast" plain text into a text object
        if( defined $args{text} ) {
            $args{text_obj} = ChangingText->new(delete $args{text});
        }

        return \%args;
    }

    sub text {
        my $self = shift;

        if( @_ ) {
            # Change the existing text object.
            $self->text_obj->set_text(shift);
            return;
        }
        else {
            return $self->text_obj;
        }
    }
}
为了完成这个错觉,BUILDARGS用于将纯文本初始化参数更改为ChangingText对象

{
    package File;
    use Moose;

    has 'text_obj' => (
        is              => 'rw',
        isa             => 'ChangingText',
        default         => sub {
            return ChangingText->new;
        }
    );

    sub BUILDARGS {
        my $class = shift;
        my %args  = @_;

        # "Cast" plain text into a text object
        if( defined $args{text} ) {
            $args{text_obj} = ChangingText->new(delete $args{text});
        }

        return \%args;
    }

    sub text {
        my $self = shift;

        if( @_ ) {
            # Change the existing text object.
            $self->text_obj->set_text(shift);
            return;
        }
        else {
            return $self->text_obj;
        }
    }
}
然后它透明地工作

my $file = File->new('text' => 'hello');

my $text = $file->text();
say $text;  # hello

$file->text('goodbye');
say $text;  # goodbye

啊,这很有道理。。。嗯,但是如果我不希望返回的值需要被取消引用呢?有没有办法将别名从子例程返回到
$text
,这样就可以像访问纯标量一样访问它?如果要返回引用,就不需要绑定。只需返回引用并根据需要进行更改。这对用户来说更简单、更快、更透明。@Schwern:你如何用一个简单的引用实现
子存储{die'Read only!'
?@choroba我错过了这一部分。用于根据需要打开和关闭只读标志。此解决方案添加了“远距离操作”<代码>$text可以在声明它的上下文之外更改。读者无法知道
$text
可以如何更改。更糟糕的是,读者会认为
$text
是一个简单的字符串,并认为它纯粹是词汇。这一切都使代码调试变得极其复杂。也许你应该问一个关于你试图用搭售来解决的问题?那太棒了!现在,我要花5个小时来思考这个过程:)我甚至不确定在12909309个不同的人告诉我这是一个可怕的做法后我是否会使用它,但感谢你为我提出的问题提供了一个有效的解决方案。