【问题标题】:How can I compare arrays in Perl?如何比较 Perl 中的数组?
【发布时间】:2011-02-05 05:05:18
【问题描述】:

我有两个数组,@a@b。我想比较两个数组的元素。

my @a = qw"abc def efg ghy klm ghn";
my @b = qw"def ghy jgk lom com klm";

如果任何元素匹配,则设置一个标志。有什么简单的方法吗?

【问题讨论】:

  • 因为你的问题不清楚,所以你得到了两种答案:(1)搜索成对匹配的那些,例如$a[$i] eq $b[$i]; (2) 搜索任何匹配的那些,例如$a[$i] eq $b[$j]。你的目标是什么?
  • @Sinan:您标记为“可能重复”的那个问题与这个不同。那是关于比较两个数组的所有元素,但这个是关于找到一个共同元素。
  • @Kinopiko 如果数组至少有一个元素不同,那么它们就不一样了。如果数组相同,则它们的元素没有区别。如果至少有一对不同,则接受的答案返回0,如果没有,则返回1

标签: perl arrays


【解决方案1】:

List::Compare

if ( scalar List::Compare->new(\@a, \@b)->get_intersection ) {
    …
}

【讨论】:

    【解决方案2】:

    首先,您的 2 个数组需要正确写入。

    @a = ("abc","def","efg","ghy","klm","ghn");
    @b = ("def","efg","ghy","klm","ghn","klm");
    

    其次,对于任意数组(例如,其元素可能引用其他数据结构的数组),您可以使用Data::Compare

    对于元素为标量的数组,您可以使用List::MoreUtils pairwise BLOCK ARRAY1 ARRAY2 进行比较,其中 BLOCK 是您的比较子例程。您可以通过以下方式模拟pairwise(如果您没有 List::MoreUtils 访问权限):

    if (@a != @b) {
        $equals = 0;
    } else {
        $equals = 1;
        foreach (my $i = 0; $i < @a; $i++) {
            # Ideally, check for undef/value comparison here as well 
            if ($a[$i] != $b[$i]) { # use "ne" if elements are strings, not numbers
                                    # Or you can use generic sub comparing 2 values
                $equals = 0;
                last;
            }
        }
    }
    

    附:我不确定,但List::Compare 可能总是对列表进行排序。我不确定它是否可以进行成对比较。

    【讨论】:

    • 你使用 scalar 的方式太随意了,不符合我的口味。
    • 标量比较运算符将标量上下文强加于其参数。所以,@a == @bscalar(@a) == scalar(@b) 相同,$i &lt; @a$i &lt; scalar(@a) 相同。
    • 这是非常主观的。我认为额外的绒毛会降低可读性(否则,我仍然会使用 Java 编程 ;-)
    • 我没有仔细检查它是否有效,但为什么@a = qw"abc def efg ghy klm ghn" 不能工作?它不会被解析为用户定义的轮廓符内的列表(在这种情况下为'"'?)它应该与qw//qw||qw()或诸如此类没有任何不同。
    • @Oesor - 你没有使用 Sinan 的 Time::Machine 模块。我的评论针对的是my @a = "abc,def,efg,ghy,klm,ghn" 的问题的原始措辞(在布赖恩的编辑之前)
    【解决方案3】:
    my @a = qw' abc def efg ghy klm ghn ';
    my @b = qw' def ghy jgk lom com klm ';
    
    my $flag;
    
    foreach  my $item(@a) {
      $flag = @b~~$item ? 0 : 1;
      last if !$flag;
    }
    

    请注意,您需要 Perl 5.10 或更高版本才能使用 smart match operator (~~)。

    【讨论】:

    • @DVK,我不知道。我从学习 Perl 中学到了这一点。我不需要使用 5.010;声明,但也许它只在 Perl 5.10 之后才可用。我会检查一下。我仍然是 Perl 学习者。如果出现任何问题,请纠正我:)
    • @DVK,书中说“Perl 5.10 的智能匹配运算符”。但看起来我不必使用 use 5.010;陈述。刚刚测试了一遍,使用5.010;至少对于 WinXP 上的 ActivePerl 5.10.0,声明是不必要的。但我想这在 Perl 5.10 之前是行不通的。
    • 虽然我没有看到任何明显的新手错误,但我认为这不会一直正常工作。
    【解决方案4】:

    根据'if any 元素匹配'的要求,使用集合的交集:

    sub set{
      my %set = map { $_, undef }, @_;
      return sort keys %set;
    }
    sub compare{
        my ($listA,$listB) = @_;
        return ( (set(@$listA)-set(@$listB)) > 0)
    }
    

    【讨论】:

    • 这个问题被标记为“perl”。我不想在没有事先警告你的情况下对你投反对票......
    • 没有问题是完全特定于语言的。我相信有人可以想出一个 perl 版本。它也说明了一般观点。
    • 是的,但是如果一个新手在这个带有 perl 标记的线程中看到你的答案,没有任何通知表明它不是 Perl 答案,然后输入它,然后想知道为什么它没有工作?与此答案有关的唯一负责任的事情是否决它。对不起。
    • @Brad:干杯,看起来不错。我很惊讶没有用于集合的内置/广泛可用的模块......
    • 我删除了我的反对票。仍然有反对票,但不是我的。
    【解决方案5】:

    这是一种方式:

    use warnings;
    use strict;
    my @a = split /,/, "abc,def,efg,ghy,klm,ghn";
    my @b = split /,/, "def,ghy,jgk,lom,com,klm";
    my $flag = 0;
    my %a;
    @a{@a} = (1) x @a;
    for (@b) {
        if ($a{$_}) {
            $flag = 1;
            last;
        }
    }
    print "$flag\n";
    

    【讨论】:

    • 不是最简单的读物,但我喜欢它。在回答问题时,我喜欢解释更多的另类行为,以便我们学习而不是烹饪。
    • 有人能解释一下“@a{@a} = (1) x @a;”吗?
    • 它用@a 的元素填充%a 的键,值都是1。这利用了Perl 允许具有相同名称的不同变量类型、哈希片和一个标量上下文中的数组。但是,此代码不适用于比较具有非唯一值的数组
    【解决方案6】:

    蛮力应该对小 a n 起作用:

    my $flag = 0;
    foreach my $i (@a) {
        foreach my $k (@b) {
            if ($i eq $k) {
                $flag = 1;
                last;
            }
        }
    }
    

    对于较大的n,使用哈希表:

    my $flag   = 0;
    my %aa     = ();
       $aa{$_} = 1 foreach (@a);
    foreach my $i (@b) {
        if ($aa{$i}) {
            $flag = 1;
            last;
        }
    }
    

    其中一个大的n|@a| + |@b| &gt; ~1000

    【讨论】:

      【解决方案7】:

      选中以创建一个 intersect 函数,该函数将返回两个列表中都存在的项目列表。那么你的返回值取决于相交列表中的项目数。

      您可以在网上轻松找到 Perl 的 intersect 的最佳实现。我记得几年前找过它。

      这是我发现的:

      我的@array1 = (1, 2, 3); 我的@array2 = (2, 3, 4); 我的 %original = (); 我的@isect = (); 地图 { $original{$_} = 1 } @array1; @isect = grep { $original{$_} } @array2;

      【讨论】:

        【解决方案8】:

        恕我直言,您应该使用List::MoreUtils::pairwise。但是,如果由于某种原因你不能,那么下面的 sub 将为第一个数组中的值与第二个数组中的值比较的每个索引返回一个 1。您可以根据需要推广此方法并传递您自己的比较器,但此时,只需安装 List::MoreUtils 将更有效地利用您的时间。

        use strict; use warnings;
        
        my @a = qw(abc def ghi jkl);
        my @b = qw(abc dgh dlkfj jkl kjj lkm);
        my $map = which_ones_equal(\@a, \@b);
        
        print join(', ', @$map), "\n";
        
        sub which_ones_equal {
            my ($x, $y, $compare) = @_;
            my $last = $#$x > $#$y ? $#$x : $#$y;
            no warnings 'uninitialized';
            return [ map { 0 + ($x->[$_] eq $y->[$_]) } $[ .. $last ];
        }
        

        【讨论】:

          【解决方案9】:

          这是 Perl。 “显而易见”的解决方案:

          my @a = qw"abc def efg ghy klm ghn";
          my @b = qw"def ghy jgk lom com klm";
          print "arrays equal\n"
              if @a == @b and join("\0", @a) eq join("\0", @b);
          

          假设“\0”不在@a 中。

          但感谢您确认除了滚动您自己没有其他通用解决方案。

          【讨论】:

            【解决方案10】:
            my @a1 = qw|a b c d|;
            my @a2 = qw|b c d e|;
            
            for my $i (0..$#a1) {
                say "element $i of array 1 was not found in array 2" 
                    unless grep {$_ eq $a1[$i]} @a2
            }
            

            【讨论】:

              【解决方案11】:

              如果您认为不同顺序的数组是不同的,您可以使用Array::Diff

              if (Array::Diff->diff(\@a, \@b)->count) {
                 # not_same
              } else {
                 # same
              }
              

              【讨论】:

                【解决方案12】:

                这个问题仍然可能意味着两件事,它指出“如果任何元素匹配,则设置一个标志”:

                1. 相同位置的元素,即 $a[2] eq $b[2]
                2. 任何位置的值,即 $a[3] eq $b[5]

                对于案例 1,您可以这样做:

                # iterate over all positions, and compare values at that position
                my @matches = grep { $a[$_] eq $b[$_] } 0 .. $#a;
                
                # set flag if there's any match at the same position 
                my $flag = 1 if @matches;
                

                对于情况 2,您可以这样做:

                # make a hash of @a and check if any @b are in there
                my %a = map { $_ => 1 } @a;
                my @matches = grep { $a{$_} } @b;
                
                # set flag if there's matches at any position 
                my $flag = 1 if @matches;
                

                请注意,在第一种情况下,@matches 保存匹配元素所在位置的索引,而在第二种情况下,@matches 保存匹配值按照它们在@b 中出现的顺序。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-07
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-06-09
                  • 1970-01-01
                  • 2011-12-23
                  相关资源
                  最近更新 更多