寻找与第二个数组元素匹配的数组元素

3

我有两个数组@uarts@txd,它们看起来像这样:

@uarts = qw(uart_1 uart_10 uart_3 uart_9 ); 
@txd = qw(PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10 PIO_uart_5 PIO_uart_9 PIO_uart_7);

我希望从@txd中仅提取包含@uarts元素之一的元素。 我编写的代码如下,但不起作用。

my @array;
for (my $i = 0 ; $i <= $#uarts ; $i++) {
  @array = grep { $_ =~ /$uarts[$i]/ } (@txd);
  print "@array\n";
}

请学会格式化你的Perl代码,使其易读。在这种情况下,我已经为您完成了,但如果您发布可理解的代码,它将有益于试图帮助您和您自己的人。 - Borodin
这次我进行了缩进。抱歉,但我无法理解。 - Grace90
好的。这比以前肯定要好,但是您的大括号缩进一级,而内容缩进一次,这种格式很不寻常。在for之后看到一个空格来区分它和函数调用也会很好。请参考perldoc perlstyle了解最常见的标准。 - Borodin
4个回答

5
我必须坦诚地说,如果你对Perl不熟悉的话,“map”和“grep”两者都很难理解,建议避免使用。它们并没有带来太多好处 - 看起来它们似乎能减少代码复杂度,但实际上是因为grep的循环隐含了。因此,你只会让你的代码更难以理解。
此外,我真的不喜欢那种for循环的风格 - 在Perl中几乎总是多余的。在上面的例子中,你只引用了当前元素(如果访问下一个或前一个元素则另当别论)。
所以,可以这样展开它:
foreach my $uart ( @uarts ) {
    foreach my $PIO ( @txd ) {
        if ( $PIO =~ m/$uart/ ) { 
            print "$PIO matches $uart\n";
        }
     }
 }

注意:此代码未进行任何唯一性测试,如果存在多个匹配项,则会出现重复。

另外,请打开use strict;use warnings;。您的数组声明不正确。

my @uarts = qw ( uart_1 uart_10 uart_3 uart_9 );
my @txd   = qw ( PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10
                 PIO_uart_5 PIO_uart_9PIO_uart_7
);

我还想指出一点——你在循环外定义了变量@array,这意味着你想要保留它。但是每次迭代都会通过赋值grep的输出来覆盖它。
我建议你将@array的作用域限制在循环内部,或者考虑使用push/popshift/unshift来添加和删除现有数组中的元素。

2
如果您需要访问下一个/上一个元素,那么类似于foreach my $i ( 0 .. $#my_array )的语句通常比传统的C风格for循环更易于阅读。 - plusplus
实际上,我已经在代码中使用了"use strict;"和"use warnings;",而这些数组uarts和txd我在这里没有声明。它们在脚本的前几行中已经存在于我的代码中。我只是提供了从"my @array "开始的一小部分代码。 - Grace90
对于您建议的代码,我想打印与 @txd 匹配的元素。 - Grace90

3
你的代码“运行”起来了。你应该总是说清楚你所谓的“不工作”的含义。
我认为的问题是,你的代码正好按照你描述的方式工作。它找到了“那些包含@uarts中任意元素的@txd元素”,而我认为你需要的是以@uarts中任意字符串结尾的那些元素。
按照现有的形式,你的程序输出:
PIO_uart_1 PIO_2_uart_1 PIO_uart_10
PIO_uart_10
PIO_uart_3
PIO_uart_9

因为uart_1PIO_uart_10的一个子字符串,所以在查找时会找到后者。如果要查找以给定uart字符串结尾的元素,只需将正则表达式添加一个行尾锚点即可,如下:

@array = grep { $_ =~ /$uarts[$i]$/ } (@txd)

这将改变输出结果为:
PIO_uart_1 PIO_2_uart_1
PIO_uart_10
PIO_uart_3
PIO_uart_9

我希望这正是您所需要的内容。

但是它可以写得更好。最好循环遍历数组的内容,除非您特别需要索引,而且没有必要将@array作为全局变量(它的命名也可以更好),因此这将适用于您。

use strict;
use warnings;

my @uarts = qw(uart_1 uart_10 uart_3 uart_9 ); 
my @txd = qw(PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10 PIO_uart_5 PIO_uart_9 PIO_uart_7);

for my $uart ( @uarts ) {
  my @matches = grep /$uart\z/, @txd;
  print "@matches\n";
}

输出

PIO_uart_1 PIO_2_uart_1
PIO_uart_10
PIO_uart_3
PIO_uart_9

确切地说,我只是最后不得不使用 $.. 谢谢 :) - Grace90

1
你可以通过将@array = grep{$_=~ /$uarts[$i]/}(@txd);更改为push @array, grep{$_=~ /$uarts[$i]/}(@txd);来修复你的代码。

但是更加明智和高效的方式是准备匹配正则表达式并进行O(N+M)而不是O(N*M)的操作。

use strict;
use warnings;

my @uarts = qw(uart_1 uart_10 uart_3 uart_9);
my @txd
    = qw(PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10 PIO_uart_5 PIO_uart_9 PIO_uart_7);

my @array = do {
    my $re = join '|', map quotemeta, @uarts;
    $re = qr/$re/;
    grep /$re/, @txd;
};

print "@array\n";

我真的怀疑这是真的:你的答案经常有问题。例如,这个答案与OP的原始代码存在相同的问题:它找到了@txd中包含任何一个字符串而不是以它们中的任何一个结尾的元素。另外,编译正则表达式只用于一次使用是没有意义的,因为grep命令会自动完成。我早就放弃了追求投票的行为——那样只会导致疯狂! - Borodin
你说得对,$re = qr/$re/; 这一行是不必要的,但是显式编译正则表达式只是一种习惯。我喜欢在代码中看到这个正则表达式是常量,因为我通常在不同的地方使用相同的正则表达式。 - Hynek -Pichi- Vychodil

-3
@uarts =(uart_1,uart_10,uart_3,uart_9 ); 
@txd =(PIO_uart_1,PIO_2_uart_1,PIO_uart_3,PIO_uart_10,PIO_uart_5,PIO_uart_9,PIO_uart_7);
my @array;
for(my $i=0;$i<=$#uarts;$i++ )
{
    @array=grep{$_=~/$uarts[$i]/}(@uarts);
    print "@array\n";
}

输出

uart_1 uart_10
uart_10
uart_3
uart_9

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接