【问题标题】:Perl: Removing array items and resizing the arrayPerl:删除数组项并调整数组大小
【发布时间】:2016-12-11 21:37:57
【问题描述】:

我正在尝试使用 Perl 中的另一个数组过滤一组术语。我在 OS X 上有 Perl 5.18.2,但如果我 use 5.010 行为是相同的。这是我的基本设置:

#!/usr/bin/perl
#use strict;
my @terms = ('alpha','beta test','gamma','delta quadrant','epsilon',
             'zeta','eta','theta chi','one iota','kappa');
my @filters = ('beta','gamma','epsilon','iota');
foreach $filter (@filters) {
    for my $ind (0 .. $#terms) {
        if (grep { /$filter/ } $terms[$ind]) {
            splice @terms,$ind,1;
        }
    }
}

这可以提取与各种搜索词匹配的行,但数组长度不会改变。如果我写出结果 @terms 数组,我得到:

[alpha]
[delta quadrant]
[zeta]
[eta]
[theta chi]
[kappa]
[]
[]
[]
[]

如您所料,打印scalar(@terms) 得到10 的结果。

我想要的是一个长度为 6 的结果数组,最后没有四个空白项。我如何得到这个结果?既然perldoc page about splice 说“数组会根据需要增长或缩小”,为什么数组没有缩小?

(我对 Perl 不是很流利,所以如果你在想“你为什么不...我听说过。)

【问题讨论】:

  • grep 对数组进行操作并返回匹配的元素。也许你的意思是$terms[$ind] =~ /$filter/ 匹配一个?
  • 是的,看起来像预期的那样工作 - 谢谢!我仍然对为什么数组没有像我之前所做的那样缩小感到困惑。
  • 从您正在迭代的数组中删除元素总是很棘手。每次拼接时,偏移量都会移动 1。
  • FWIW, use VERSION 仅指定所需的最低版本;它不模拟 Perl 解释器,因为它存在于该版本中。

标签: arrays perl array-difference


【解决方案1】:

你总是可以重新生成数组减去你不想要的东西。 grep 充当过滤器,让您决定想要哪些元素,不要哪些元素:

#!/usr/bin/perl

use strict;

my @terms = ('alpha','beta test','gamma','delta quadrant','epsilon',
           'zeta','eta','theta chi','one iota','kappa');
my @filters = ('beta','gamma','epsilon','iota');

my %filter_exclusion = map { $_ => 1 } @filters;

my @filtered = grep { !$filter_exclusion{$_} } @terms;

print join(',', @filtered) . "\n";

如果你手头有一个像%filter_exclusion 这样的简单结构,那就很容易了。

更新:如果要允许任意子字符串匹配:

my $filter_exclusion = join '|', map quotemeta, @filters;

my @filtered = grep { !/$filter_exclusion/ } @terms;

【讨论】:

  • 那个只是部分有效——它过滤掉了gammaepsilon,但不是beta testone iota。不过,对于未来的项目很有用!
  • 添加了一个测试任意子字符串的版本。这个再次使用正则表达式,但每个条目只测试一次,而不是 N 次测试。
  • 酷,谢谢!这确实有效。请注意,我不知道它是如何或为什么起作用的。
  • grep 在这里对@terms 中的每个元素都起到了通过-失败过滤器的作用,因此对于来自@terms 的给定$_,它会测试它是否与该模式匹配。模式只是一个正则表达式,可以匹配其中任何一个作为子字符串。
【解决方案2】:

要查看发生了什么,请在每个步骤中打印数组的内容:当您拼接数组时,它会缩小,但您的循环会迭代 0 .. $#terms,所以在循环结束时,$ind将指向数组末尾的后面。当您使用grep { ... } $array[ $too_large ] 时,Perl 需要在 grep 块内将不存在的元素别名为 $_,因此它会在数组中创建一个 undef 元素。

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

my @terms = ('alpha', 'beta test', 'gamma', 'delta quadrant', 'epsilon',
             'zeta', 'eta', 'theta chi', 'one iota', 'kappa');
my @filters = qw( beta gamma epsilon iota );

for my $filter (@filters) {
    say $filter;
    for my $ind (0 .. $#terms) {
        if (grep { do {
            no warnings 'uninitialized';
            /$filter/
        } } $terms[$ind]
        ) {
            splice @terms, $ind, 1;
        }
        say "\t$ind\t", join ' ', map $_ || '-', @terms;
    }
}

如果您使用$terms[$ind] =~ /$filter/ 而不是grep,您仍然会收到未初始化的警告,但由于不需要为元素命名,因此不会创建它。

【讨论】:

  • @ikegami:我在输出中没有看到gamma。此外,这不是“修复”,它应该只说明创建尾随元素的原因和时间 - 因此,它们仍然存在。
  • @ikegami:如果我print "@terms",我看到alpha delta quadrant zeta eta theta chi kappa
  • 抱歉,如果您以@terms = qw( gamma gamma kappa ); 开头,则会出现此错误。第二个 gamma 被移动到 $terms[0],没有被重新访问。
  • @ikegami:没错,你是对的。但我只是想解释为什么存在 undef。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-10
  • 1970-01-01
  • 2017-08-24
  • 1970-01-01
  • 2011-06-19
相关资源
最近更新 更多