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

时间:2021-10-05 07:55:13

I have an array @test. What's the best way to check if each element of the array is the same string?

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

I know I can do it with a foreach loop but is there a better way to do this? I checked out the map function but I'm not sure if that's what I need.

我知道我可以用foreach循环来做,但是有更好的方法吗?我检查了map函数,但我不确定这是否是我需要的。

6 个解决方案

#1


11  

If the string is known, you can use grep in scalar context:

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

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

Otherwise, use a hash:

否则,使用一个散列:

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

or a shorter version:

或更短的版本:

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

NOTE: The undefined value behaves like the empty string ("") when used as a string in Perl. Therefore, the checks will return true if the array contains only empty strings and undefs.

注意:未定义的值在Perl中用作字符串时的行为与空字符串(“”)类似。因此,如果数组只包含空字符串和undefs,那么检查将返回true。

Here's a solution that takes this into account:

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

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;
}

#2


10  

Both methods in the accepted post give you the wrong answer if @test = (undef, ''). That is, they declare an undefined value to be equal to the empty string.

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

That might be acceptable. In addition, using grep goes through all elements of the array even if a mismatch is found early on and using the hash more than doubles the memory used by elements of array. Neither of these would be a problem if you have small arrays. And, grep is likely to be fast enough for reasonable list sizes.

这可能是可以接受的。此外,使用grep可以遍历数组的所有元素,即使在早期发现了不匹配,并且使用哈希比数组元素使用的内存增加了一倍以上。如果有小数组,这两个都不是问题。而且,grep可能足够快以满足合理的列表大小。

However, here is an alternative that 1) returns false for (undef, '') and (undef, 0), 2) does not increase the memory footprint of your program and 3) short-circuits as soon as a mismatch is found:

但是,这里有一种替代方法:1)返回false for (undef,”)和(undef, 0), 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;
}

However, using List::MoreUtils::first_index is likely to be faster:

但是,使用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;
}

#3


4  

TIMTOWTDI, and I've been reading a lot of Mark Jason Dominus lately.

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";
}

#4


3  

You can check how many times the element in the array (@test) is repeated by counting it in a hash (%seen). You can check how many keys ($size) are present in the hash (%seen). If more than 1 key is present, you know that the elements in the array are not identical.

您可以通过在散列中计数(%seen)来检查数组(@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;
    }
}

#5


2  

I use List::Util::first for all similar purposes.

我使用List::Util:::首先用于所有类似的用途。

# 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;

I used map \$_, @test here to avoid problems with values that evaluate to false.

我在这里使用map \$_ @test来避免计算值为false的问题。

Note. As cjm noted fairly, using map defeats the advantage of first short-circuiting. So I tip my hat to Sinan with his first_index solution.

请注意。正如cjm所指出的,使用map击败了第一次短路的优势。所以我向Sinan推荐他的first_index解决方案。

#6


2  

I think, we can use List::MoreUtils qw(uniq)

我想,我们可以用List: MoreUtils qw(uniq)

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

#1


11  

If the string is known, you can use grep in scalar context:

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

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

Otherwise, use a hash:

否则,使用一个散列:

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

or a shorter version:

或更短的版本:

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

NOTE: The undefined value behaves like the empty string ("") when used as a string in Perl. Therefore, the checks will return true if the array contains only empty strings and undefs.

注意:未定义的值在Perl中用作字符串时的行为与空字符串(“”)类似。因此,如果数组只包含空字符串和undefs,那么检查将返回true。

Here's a solution that takes this into account:

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

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;
}

#2


10  

Both methods in the accepted post give you the wrong answer if @test = (undef, ''). That is, they declare an undefined value to be equal to the empty string.

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

That might be acceptable. In addition, using grep goes through all elements of the array even if a mismatch is found early on and using the hash more than doubles the memory used by elements of array. Neither of these would be a problem if you have small arrays. And, grep is likely to be fast enough for reasonable list sizes.

这可能是可以接受的。此外,使用grep可以遍历数组的所有元素,即使在早期发现了不匹配,并且使用哈希比数组元素使用的内存增加了一倍以上。如果有小数组,这两个都不是问题。而且,grep可能足够快以满足合理的列表大小。

However, here is an alternative that 1) returns false for (undef, '') and (undef, 0), 2) does not increase the memory footprint of your program and 3) short-circuits as soon as a mismatch is found:

但是,这里有一种替代方法:1)返回false for (undef,”)和(undef, 0), 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;
}

However, using List::MoreUtils::first_index is likely to be faster:

但是,使用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;
}

#3


4  

TIMTOWTDI, and I've been reading a lot of Mark Jason Dominus lately.

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";
}

#4


3  

You can check how many times the element in the array (@test) is repeated by counting it in a hash (%seen). You can check how many keys ($size) are present in the hash (%seen). If more than 1 key is present, you know that the elements in the array are not identical.

您可以通过在散列中计数(%seen)来检查数组(@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;
    }
}

#5


2  

I use List::Util::first for all similar purposes.

我使用List::Util:::首先用于所有类似的用途。

# 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;

I used map \$_, @test here to avoid problems with values that evaluate to false.

我在这里使用map \$_ @test来避免计算值为false的问题。

Note. As cjm noted fairly, using map defeats the advantage of first short-circuiting. So I tip my hat to Sinan with his first_index solution.

请注意。正如cjm所指出的,使用map击败了第一次短路的优势。所以我向Sinan推荐他的first_index解决方案。

#6


2  

I think, we can use List::MoreUtils qw(uniq)

我想,我们可以用List: MoreUtils qw(uniq)

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