【问题标题】:Confrontation between two arrays两个阵列之间的对抗
【发布时间】:2013-08-22 01:05:34
【问题描述】:

我有一个二维数组:

array[0][0]=1
array[0][1]=2
array[0][2]=4
array[1][0]=0
array[1][1]=2

我想检查 array[0][$i] 和 array[1][$j] 之间的至少一个元素是否相同。我想了很长的路要走,因为

for my $i (0..($#{$array[0]}+1)){
    for my $j(0..($#{$array[1]}+1)){
      if (array[0][$i]==array[1][$j]){
          say "There is a match";
      }
    }
 }

是否有可能找到更好的方法?如果我有一个更大的数组,有没有办法在两个数组共享至少一个元素时停止循环?

【问题讨论】:

    标签: perl


    【解决方案1】:

    您可以使用Array::Utils 中的intersect 来查找两个子数组的交集。如果结果列表为空,则没有公共元素。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use feature 'say';
    
    use Array::Utils qw(intersect);
    
    my @array = (
        [ 1, 2, 4 ],
        [ 0, 2 ]
    );  
    
    say "There is a match" if intersect(@{$array[0]}, @{$array[1]});
    

    如果您想计算匹配数,只需在标量上下文中调用 intersect

    my $count = intersect(@{$array[0]}, @{$array[1]});
    

    关于性能的注意事项:我运行了一个基准测试,将我的答案与amon's solution 进行比较,使用List::MoreUtilsanyintersect 从水中吹了几个数量级。这并不令人意外,因为anyintersect 做了完全不同的事情:any 在找到单个匹配项后立即停止,而intersect 找到每个 匹配项。如果您只关心匹配是否存在,我肯定会推荐 any 方法,因为它对于任何非平凡数据集都会显着加快。 intersect 仅在您需要匹配列表时才有用。

    【讨论】:

    • 如果有匹配,我想关联一个控制标量;我将最后一行写为my $count=0; $count =1 if intersect(@{$array[0]}, @{$array[1]}){ say $count; },但出现以下错误syntax error at line 19, near "){";如何将匹配项与标量相关联?
    【解决方案2】:

    您可以跳出(标记的)循环。请注意,您的代码是错误的,因为数组是从零开始索引的。你想从0循环到$#array,而不是从1$#array + 1,恰好是scalar @array(除非你摆弄某些变量)。

    my $found_same = 0;
    my ($x, $y) = @array[0, 1];  # $x, $y are arrayrefs we want to loop over
    my ($x_i, $y_i);
    
    INDEX:
    for $x_i (0 .. $#$x) {
      for $y_i (0 .. $#$y) {
        if ($x->[$x_i] == $y->[$y_i]) {
          $found_same = 1;
          last INDEX;
        }
      }
    }
    
    say "found same value $x->[$x_i] at indices $x_i, $y_i" if $found_same;
    

    如果您对索引不感兴趣,仅在存在相同值的情况下,您应该循环遍历这些值:

    my $found_same = 0;
    my ($x, $y) = @array[0, 1];
    INDEX:
    for my $x_val (@$x) {
      for my $y_val (@$y) {
        if ($x_val = $y_val) {
          $found_same = 1;
          last INDEX;
        }
      }
    }
    
    say "found some same value" if $found_same;
    

    如果我们把它写成一个函数,我们可以去掉丑陋的变量重新赋值,并且可以return 打破循环:

    my $find_same = sub {
      my ($x, $y) = @_;
      for my $x_val (@$x) {
        for my $y_val (@$y) {
          return 1 if $x_val == $y_val;
        }
      }
      return 0;
    };
    
    say "found some same value" if $find_same->(@array[0, 1]);
    

    List::MoreUtils的帮助下,有一个很短的写法:

    use List::MoreUtils 'any';
    
    my ($x, $y) = @array[0, 1];
    say "found some same value" if any { my $val = $_; any { $val == $_ } @$y } @$x;
    

    所有这些解决方案都假设您只有数字数据。

    【讨论】:

    • 我不知道为什么,当我尝试第三个代码时,我安装了List::UtilList::MoreUtils后,总是出现错误:"any" is not exported by the List::Util module
    • @ValerioD.Ciotti 纠正了错字:我的意思是use List::MoreUtils 'any'
    【解决方案3】:

    此代码可能回答了不同的问题。这将检查相同的值是否在数组中的任何位置出现两次。

    哈希有助于检查一个值是否已经被看到。这里的两个代码块都使用数组中的值作为哈希键。下面的第一个代码计算一个数组值被看到的次数,并在它超过一个时报告。第二个块保存值出现位置的索引,因此它可以报告找到值的两对索引。

    my %values;
    INDEX:
    for my $ii (0 .. $#array) {
        for my $jj ( 0..($#{$array[$ii]})){
            my $val = $array[$ii][$jj];
            if ( ++$values{$val} > 1 ) {
                say "found same value $val at indices $ii, $jj";
                last INDEX;
            }
        }
    }
    
    my %indices;
    INDEX:
    for my $ii (0 .. $#array) {
        for my $jj ( 0..($#{$array[$ii]})){
            my $val = $array[$ii][$jj];
            if ( defined $indices{$val} ) {
                say "found same value $val at indices $indices{$val} and at $ii, $jj";
                last INDEX;
            }
            $indices{$val} = "$ii, $jj";
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多