2

对于没有故事的问题,请跳到行后。

我在胡闹,将一串可能出现的字母和数字拆分为 hashref 中的两个字段。只有当该字段存在时,它们才应该出现。字符串可能如下所示:/^\D*\d*$/,例如ZR17, R15, -19, 22

我不想像这样简单地把它放在两个变量中,因为实际的 hashref 有点长,我想把东西放在一起。

my $asdf = "ZR17";
my ($x, $y) = $asdf =~ m/^(\D*)(\d*)$/;
my $foo = {
  foo => $x,
  bar => $y
};

如果我不想foo在 string 的情况下没有密钥17,我可以说:

my $foo = {
  ( $x ? ( foo => $x ) : () ),
  ( $y ? ( bar => $y ) : () ),
};

我想出把它全部放在 hashref 赋值中,如下所示:

my $asdf = "ZR17";

my $foo = {
  ( $asdf =~ m/(\d+)/ ? ( foo => $1 ) : () ),
  ( $asdf =~ m/(\D+)/ ? ( bar => $1 ) : () ),
};

print Dumper $foo;

这会产生以下结果:

$VAR1 = {
          'bar' => 'ZR',
          'foo' => 'ZR'
        };

不知怎的,这里似乎只有一个$1,而且混在一起了。如果我把第二行去掉,foo将是17.

有人可以解释这里发生了什么吗?迷路/困惑在哪里?$1

4

4 回答 4

7

根据 perldoc ( http://perldoc.perl.org/perlre.html ):

These special variables, like ... the numbered match variables ($1 , $2 , $3 
, etc.) are dynamically scoped until the end of the enclosing block or until 
the next successful match, whichever comes first.

因此,$1 在 $asdf =~ m/(\d+)/ 之后已被覆盖为 17,因为它找到了匹配但尚未遇到封闭块的末尾。

然而这,

my $foo = {
  ( eval{$asdf =~ m/(\D+)/ ? ( bar => $1 ) : ()} ),
  ( eval{$asdf =~ m/(\d+)/ ? ( foo => $1 ) : ()} ),
};

将给出预期的结果,因为范围是分开的。

于 2013-06-03T12:55:27.103 回答
4

Perl 5.10+ 将允许您使用命名捕获,这基本上是您想要做的。任何不匹配的捕获组都将作为您案例中的值存储在%+其中""

use strict;
use warnings;
use Data::Dump 'dd';

my $asdf = "ZR17";
$asdf =~ m/^(?<alpha>\D*)(?<num>\d*)$/;

my $foo = { map { $+{$_} ? ( $_ => $+{$_} ) : () } keys %+ };

dd $foo;  # { alpha => "ZR", num => 17 }
于 2013-06-03T13:22:26.833 回答
3

它看起来像并且来自$1上一次正则表达式匹配,foobar

my $asdf = "ZR17";

my $foo = {
  ( $asdf =~ m/(\D+)/ ? ( bar => $1 ) : () ),
  ( $asdf =~ m/(\d+)/ ? ( foo => $1 ) : () ),
};

print Dumper $foo;

输出

$VAR1 = {
      'bar' => '17',
      'foo' => '17'
    };

然而,这按预期工作,

my $foo = {
  ( map { (bar => $_) } $asdf =~ m/(\D+)/ ),
  ( map { (foo => $_) } $asdf =~ m/(\d+)/ ),
};

输出

$VAR1 = {
      'bar' => 'ZR',
      'foo' => '17'
    };
于 2013-06-03T12:46:16.753 回答
1

$1我猜三元运算符在返回( bar => $1 )and时不会进行评估( foo => $1 )。所以在中间步骤你得到

$foo = { ( bar => $1 ), ( foo => $1 ) };

并且由于$1现在是第二次匹配操作的捕获子字符串,因此您将获得相同的值$foo{bar}$foo{foo}


实现您想要的另一种方法(即,如果未找到相应的匹配项,则哈希元素不存在):

my %patt = {
    foo => '(\d+)',
    bar => '(\D+)',
};

my %foo = map { $_ => $1 if $asdf =~ /$patt{$_}/ } keys %patt;

您可以%patt在需要匹配更多模式时进行扩展。

于 2013-06-06T14:31:19.763 回答