Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.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 如何在使用散列或数组元素时绕过魔钩_Perl - Fatal编程技术网

Perl 如何在使用散列或数组元素时绕过魔钩

Perl 如何在使用散列或数组元素时绕过魔钩,perl,Perl,我试图使用Variable::Magic来捕获哈希元素被修改的时间: use Variable::Magic qw(cast wizard); my %h = (a => 1, b => 2); cast %h, wizard store => sub { warn "store: @_\n"; my $k = $_[2]; cast $_[0]{$k}, wizard set => sub { warn "$k set to ${

我试图使用
Variable::Magic
来捕获哈希元素被修改的时间:

use Variable::Magic qw(cast wizard);
my %h = (a => 1, b => 2);
cast %h, wizard store => sub {
    warn "store: @_\n";
    my $k = $_[2];
    cast $_[0]{$k}, wizard set => sub {
        warn "$k set to ${$_[0]}\n"
    }
};
$h{a} = 33;
但是,哈希元素上的第二个内部
cast
将触发哈希中的
store
魔术,并进入无限递归(并崩溃)

我找到的唯一方法是使用通过
cast
附加的数据作为锁/标志:

use Variable::Magic qw(cast wizard);
cast %h, wizard
    data => sub {0},
    store => sub {
        return if $_[1]++;
        my $k = $_[2];
        cast $_[0]{$k}, wizard set => sub {
            warn "$k set to ${$_[0]}\n"
        };
        $_[1] = 0;
    }
;
$h{a} = 33;
这看起来既笨拙又愚蠢,我觉得我错过了一些显而易见的东西

有没有更好的方法?我在方法中寻找了一些选项来
Variable::Magic::wizard
屏蔽它自己的魔力,但我没有找到任何东西


注意:我不想使用重量级的
tie
,也不想使用其他一些不能用简单的
apt-get安装的XS模块;如果需要的话,我可以编写自己的XS,在那里一切都会简单得多,但我正试图避免这种情况。

store
是在哈希元素作为左值获取时调用的,所以
$h{$k}=…
f($h{$k})
\$h{$k}
调用
store code>。因此,虽然在值更改后调用
set
,但在值更改前必须调用
store

事实上,它是在提取元素之前调用的,因此它是在元素不存在时创建元素之前调用的。这是非常不幸的,因为这意味着一个简单的
检查是不够的

因为神奇的回调是在“错误的时间”(当元素根本不存在时)调用的,所以像您这样的解决方案(即使用标志)是我能想出的最好办法

也就是说,即使发生异常,您也应该确保重置标志。这可以使用
local
完成


您的目标似乎是为散列的每个元素添加一些魔力,包括稍后创建的元素。为此,我将使用以下方法:

use Variable::Magic qw( cast wizard );

my $hash_ele_wiz = wizard(
    set => sub {
        my ($ref) = @_;
        say "store: $$ref";
    },
);

my $hash_wiz = wizard(
    data => sub {
        my ($var) = @_;
        cast($_, $hash_ele_wiz) for values(%$var);
        return { nomg => 0 };
    },
    store => sub {
        my ($var, $data, $key) = @_;
        return if exists($var->{$key}) || $data->{nomg};
        my $ele_ref = do { local $data->{nomg} = 1; \( $var->{$key} ) };
        cast($$ele_ref, $hash_ele_wiz);
    },
);

my %h = ( a => 1, b => 2 );
cast(%h, $hash_wiz);

$h{b} = 3;          # store: 3
$h{c} = 4;          # store: 4
my $ref = \$h{d};
$$ref = 5;          # store: 5

不需要太多的扩展就可以递归地添加魔法。请参阅。

提示中的“递归地在数据结构上施展魔法:
returnif$\uU1];本地美元[1]=1会更安全提示:不要每次调用
cast
时创建新向导!当然,那是为了使事情简短;特别是对于内部
cast
,键名可以作为参数传递给,而不是通过闭包传递:
cast${$k},$keywiz,$k,其中
my$keywiz=wizard data=>sub{$\[1]},设置=>sub{warn”$\[1]作为不安全的
$\[1]++
,这是一个错误,但最好还是保持原样,因为这是一个问题;-)既然你把答案变成了一个示例,如果你也从元素向导中把键名传递给
set
方法就好了。你可以把它作为一个参数传递给
cast
,它会被传递给
数据
。我知道——我已经知道了在a中提到。我的观点是,最好将其添加到答案中。确切的解决方案取决于该解决方案试图解决的确切问题。由于该问题与另一个问题有关,因此它缺少提供实现的必要信息。尝试将此离题信息添加到答案中只会降低难度答案的y。答案的要点是应该使用
local
而不是set/unset括号。您确实将其转换为示例;可以随意拒绝、编辑样式等等。