如何在Perl中检查数组的所有元素是否相同?

时间:2010-02-21 09:53:44

标签: perl arrays

我有一个数组@test。检查数组中每个元素是否是同一个字符串的最佳方法是什么?

我知道我可以使用foreach循环执行此操作,但是有更好的方法吗?我检查了地图功能,但我不确定这是否是我需要的。

6 个答案:

答案 0 :(得分:14)

如果字符串已知,则可以在标量上下文中使用grep

if (@test == grep { $_ eq $string } @test) {
 # all equal
}

否则,请使用哈希:

my %string = map { $_, 1 } @test;
if (keys %string == 1) {
 # all equal
}

或更短的版本:

if (keys %{{ map {$_, 1} @test }} == 1) {
 # all equal
}

注意:undef的值在Perl中用作字符串时的行为类似于空字符串("")。因此,如果数组只包含空字符串和undef s。

,则检查将返回true

这是一个考虑到这一点的解决方案:

my $is_equal = 0;
my $string   = $test[0]; # the first element

for my $i (0..$#test) {
    last unless defined $string == defined $test[$i];
    last if defined $test[$i] && $test[$i] ne $string;
    $is_equal = 1 if $i == $#test;
}

答案 1 :(得分:10)

如果@test = (undef, ''),接受的帖子中的两种方法都会给出错误答案。也就是说,它们声明一个未定义的值等于空字符串。

这可能是可以接受的。此外,使用grep遍历数组的所有元素,即使在早期发现不匹配并使用散列比使用数组元素使用的内存多一倍。如果您有小数组,这些都不会成为问题。而且,grep对于合理的列表大小来说可能足够快。

但是,这里有一个替代方案,1)为(undef, '')(undef, 0)返回false,2)不会增加程序的内存占用,3)一旦不匹配就会发生短路实测值:

#!/usr/bin/perl

use strict; use warnings;

# Returns true for an empty array as there exist
# no elements of an empty set that are different
# than each other (see
# http://en.wikipedia.org/wiki/Vacuous_truth)

sub all_the_same {
    my ($ref) = @_;
    return 1 unless @$ref;
    my $cmpv = \ $ref->[-1];
    for my $i (0 .. $#$ref - 1)  {
        my $this = \ $ref->[$i];
        return unless defined $$cmpv == defined $$this;
        return if defined $$this
            and ( $$cmpv ne $$this );
    }
    return 1;
}

但是,使用List::MoreUtils::first_index可能会更快:

use List::MoreUtils qw( first_index );

sub all_the_same {
    my ($ref) = @_;
    my $first = \ $ref->[0];
    return -1 == first_index {
        (defined $$first != defined)
            or (defined and $_ ne $$first)
    } @$ref;
}

答案 2 :(得分:4)

TIMTOWTDI,我最近一直在阅读很多Mark Jason Dominus

use strict;
use warnings;

sub all_the_same {
    my $ref = shift;
    return 1 unless @$ref;
    my $cmp = $ref->[0];
    my $equal = defined $cmp ?
        sub { defined($_[0]) and $_[0] eq $cmp } :
        sub { not defined $_[0] };
    for my $v (@$ref){
        return 0 unless $equal->($v);
    }
    return 1;
}

my @tests = (
    [ qw(foo foo foo) ],
    [ '', '', ''],
    [ undef, undef, undef ],
    [ qw(foo foo bar) ],
    [ '', undef ],
    [ undef, '' ]
);

for my $i (0 .. $#tests){
    print "$i. ", all_the_same($tests[$i]) ? 'equal' : '', "\n";
}

答案 3 :(得分:3)

您可以通过计算哈希值(看到%)来检查数组中元素(@test)重复的次数。您可以检查哈希中存在多少个键($ size)(看到%)。如果存在多个键,则表示数组中的元素不相同。

sub all_the_same {
    my @test = @_;
    my %seen;
    foreach my $item (@test){
      $seen{$item}++
    }
    my $size = keys %seen;
    if ($size == 1){
        return 1;
    }
    else{
        return 0;
    }
}

答案 4 :(得分:2)

我将List::Util::first用于所有类似用途。

# try #0: $ok = !first { $_ ne $string } @test;
# try #1: $ok = !first { (defined $_ != defined $string) || !/\A\Q$string\E\z/ } @test;

# final solution
use List::Util 'first';
my $str = shift @test;
my $ok = !first { defined $$_ != defined $str || defined $str && $$_ ne $str } map \$_, @test;

我在这里使用map \$_, @test来避免评估为false的值出现问题。

注意。正如cjm公平地指出的那样,使用map会破坏第一次短路的优势。所以我用他的first_index解决方案向Sinan倾诉。

答案 5 :(得分:2)

我认为,我们可以使用List :: MoreUtils qw(uniq)

my @uniq_array = uniq @array;
my $array_length = @uniq_array;
$array_length == 1 ? return 1 : return 0;