如何在Perl中使多个变量为只读?

如何在Perl中使多个变量为只读?,perl,Perl,在Perl中,编写代码是惯用的 $_ = '2021-04-09 10:23:42'; my ($year, $month, $day) = m/(\d\d\d\d)-(\d\d)-(\d\d)/; 我怎么能用这个?正在尝试Readonly::Array Readonly::Array my ($year, $month, $day) => m/(\d\d\d\d)-(\d\d)-(\d\d)/; 导致 Type of arg 1 to Readonly::Array must be

在Perl中,编写代码是惯用的

$_ = '2021-04-09 10:23:42';
my ($year, $month, $day) = m/(\d\d\d\d)-(\d\d)-(\d\d)/;
我怎么能用这个?正在尝试
Readonly::Array

Readonly::Array my ($year, $month, $day) => m/(\d\d\d\d)-(\d\d)-(\d\d)/;
导致

Type of arg 1 to Readonly::Array must be array (not list) at rdonly.pl line 4, near "m/(\d\d\d\d)-(\d\d)-(\d\d)/;"
1 at rdonly.pl line 9.
正在尝试
Readonly::Scalar

Readonly::Scalar my ($year, $month, $day) => m/(\d\d\d\d)-(\d\d)-(\d\d)/;
++$year;
die $year unless $year == 2022;
导致

Type of arg 1 to Readonly::Array must be array (not list) at rdonly.pl line 4, near "m/(\d\d\d\d)-(\d\d)-(\d\d)/;"
1 at rdonly.pl line 9.

($year不是只读的,并且具有不正确的值)

我没有一种干净的方法来执行此操作。但是,将(或在较旧的perl上)和
Readonly
结合起来就可以了,尽管看起来很脏:

use Readonly;

$_ = '2021-04-09 10:23:42';

\(my @ymd) = (\my $year, \my $month, \my $day);
my @matching = m/(\d\d\d\d)-(\d\d)-(\d\d)/;
map { Readonly::Scalar $ymd[$_] => $matching[$_] } 0 .. $#ymd;

print "$year/$month/$day"; # print 2021/04/09
++$year; # dies with "Modification of a read-only value attempted at tmp.pl line ..."
结果与您想要的非常相似。特别是
@ymd
的内容也是只读的,这意味着您不会惊讶地发现
$year
是通过
@ymd
修改的

使用允许替换

map { Readonly::Scalar $ymd[$_] => $matching[$_] } 0 .. $#ymd;

看起来好一点

如果不想使用重构功能,可以使用
Data::Alias
:只需替换
\(my@ymd)=(\my$year、\my$month、\my$day)别名my@ymd=my($year,$month,$day)

最后,有几种方法可以将代码放入
sub
中,以便稍微分解代码并提高可读性。例如:

$_ = '2021-04-09 10:23:42';

init_readonly(my $year, my $month, my $day,
              [m/(\d\d\d\d)-(\d\d)-(\d\d)/]);

print "$year/$month/$day";
++$year;
die $year unless $year == 2022;

sub init_readonly {
    my $values = pop @_;
    pairwise { Readonly::Scalar $a => $b } @_, @$values;
}

Data::Alias
不再需要,因为在子系统中,
@
包含参数的别名)

接口1

Readonly::ManyScalars my($year,$month,$day)=>
或死亡(“无效输入\n”);
while(只读::我的许多刻度($k,$v)=>[每个%h]){
...
}
实施:

使用只读qw();
子只读::多刻度{
我的$VAL=pop(@41;;
对于(0..$##){
只读::标量$\u[$\ u]=>$VAL->[$\u];
}
#尽可能类似于列表分配。
返回wantarray?@@@0+@$VAL;
}
此版本的优点是与现有的只读SUB类似。请注意,您需要根据指定的值创建一个数组(例如,如上图所示,将表达式括在方括号中)


接口2

(ro我的$year,ro我的$month,ro我的$day)=/^(\d\d\d\d)-(\d\d)-(\d\d)\z/
或死亡(“无效输入\n”);
while((ro我的$k,ro我的$v)=每个(%h)){
...
}
实施:

使用只读qw();
使用变量::Magic qw(向导cast);
my$wiz=向导(
data=>sub{[0,$\[1]},
set=>sub{
只读::标量(${$\[1][1]},${$\[0]});
$_[1][0] = 1;
},
免费=>sub{
只读::标量(${$\[1][1]},未定义)如果!$\[1][0];
},
);
子ro(\$):左值{cast(my$proxy,$wiz,$[0]);$proxy}

此版本的优点是并非所有变量都必须是只读的,您可以使用
(ro my$x,unde,ro my$z)=……
而不是使用虚拟变量。

另外两个技巧:)
my@values=@{pop@}
对每个元素进行不必要的复制,
init\u readonly
应该返回
@values
中的值的数量,因此
init\u readonly
可以在
if
或die
中使用。我正要建议
readonly::Scalar${$->[0]=>$->[1]for+zip[\my($year,$month,$date)],[m]/(…)-(…)-(…)/;
,但我认为最新的编辑非常相似。谢谢@ikegami;那
{pop}
确实是草率的,让我们假设我没有写:x关于返回
@values
中元素数的好观点;
成对的
已经这样做了,而你的答案已经展示了这一点,所以我想我的答案与你的一样:D(为了将来的读者,请不要删除你的评论吗?)不要干涉巫师的事情:)