In Perl, is there a built in way to compare two arrays for equality?

后端 未结 13 2312
无人及你
无人及你 2020-11-27 17:08

I have two arrays of strings that I would like to compare for equality:

my @array1 = (\"part1\", \"part2\", \"part3\", \"part4\");
my @array2 = (\"part1\", \         


        
相关标签:
13条回答
  • 2020-11-27 17:21

    Perl 5.10 gives you the smart match operator.

    use 5.010;
    
    if( @array1 ~~ @array2 )
    {
        say "The arrays are the same";
    }
    

    Otherwise, as you said, you'll have top roll your own.

    0 讨论(0)
  • 2020-11-27 17:22

    There's Test::More's is_deeply() function, which will also display exactly where the structures differ, or Test::Deep's eq_deeply(), which doesn't require a test harness (and just returns true or false).

    0 讨论(0)
  • 2020-11-27 17:29

    There is the new smart match operator:

    #!/usr/bin/perl
    
    use 5.010;
    use strict;
    use warnings;
    
    my @x = (1, 2, 3);
    my @y = qw(1 2 3);
    
    say "[@x] and [@y] match" if @x ~~ @y;
    

    Regarding Array::Compare:

    Internally the comparator compares the two arrays by using join to turn both arrays into strings and comparing the strings using eq.

    I guess that is a valid method, but so long as we are using string comparisons, I would much rather use something like:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use List::AllUtils qw( each_arrayref );
    
    my @x = qw(1 2 3);
    my @y = (1, 2, 3);
    
    print "[@x] and [@y] match\n" if elementwise_eq( \(@x, @y) );
    
    sub elementwise_eq {
        my ($xref, $yref) = @_;
        return unless  @$xref == @$yref;
    
        my $it = each_arrayref($xref, $yref);
        while ( my ($x, $y) = $it->() ) {
            return unless $x eq $y;
        }
        return 1;
    }
    

    If the arrays you are comparing are large, joining them is going to do a lot of work and consume a lot of memory than just comparing each element one by one.

    Update: Of course, one should test such statements. Simple benchmarks:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use Array::Compare;
    use Benchmark qw( cmpthese );
    use List::AllUtils qw( each_arrayref );
    
    my @x = 1 .. 1_000;
    my @y = map { "$_" } 1 .. 1_000;
    
    my $comp = Array::Compare->new;
    
    cmpthese -5, {
        iterator => sub { my $r = elementwise_eq(\(@x, @y)) },
        array_comp => sub { my $r = $comp->compare(\(@x, @y)) },
    };
    

    This is the worst case scenario where elementwise_eq has to go through each and every element in both arrays 1_000 times and it shows:

                 Rate   iterator array_comp
    iterator    246/s         --       -75%
    array_comp 1002/s       308%         --
    

    On the other hand, the best case scenario is:

    my @x = map { rand } 1 .. 1_000;
    my @y = map { rand } 1 .. 1_000;
    
                  Rate array_comp   iterator
    array_comp   919/s         --       -98%
    iterator   52600/s      5622%         --
    

    iterator performance drops quite quickly, however:

    my @x = 1 .. 20, map { rand } 1 .. 1_000;
    my @y = 1 .. 20, map { rand } 1 .. 1_000;
    
                  Rate   iterator array_comp
    iterator   10014/s         --       -23%
    array_comp 13071/s        31%         --
    

    I did not look at memory utilization.

    0 讨论(0)
  • 2020-11-27 17:34

    If casing is the only difference, you can simply use:

    if (lc "@array1" eq lc "@array2") {...}
    

    Whereas "@array1" returns the same as join ( " ", @array1 )

    0 讨论(0)
  • 2020-11-27 17:35

    Not built-in, but there is Array::Compare.

    This is one of the operations that's left out of the Perl core for what I believe are didactic reasons -- that is, if you're trying to do it, there's probably something wrong. The most illustrative example of this, I think, is the absence of a core read_entire_file function; basically, providing that function in the core would lead people to think it's a good idea to do that, but instead, Perl is designed in a way that gently nudges you toward processing files line-at-a-time, which is generally far more efficient and otherwise a better idea, but novice programmers are rarely comfortable with it and they need some encouragement to get there.

    The same applies here: there is probably a much better way to make the determination you're trying to accomplish by comparing two arrays. Not necessarily, but probably. So Perl is nudging you to think about other ways of accomplishing your goal.

    0 讨论(0)
  • 2020-11-27 17:35

    Simpler solution is faster:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use Array::Compare;
    use Benchmark qw( cmpthese );
    use List::AllUtils qw( each_arrayref );
    
    my @x = 1 .. 1_000;
    my @y = map { "$_" } 1 .. 1_000;
    
    my $comp = Array::Compare->new;
    
    cmpthese -2, {
        iterator => sub { my $r = elementwise_eq(\(@x, @y)) },
        my_comp => sub { my $r = my_comp(\(@x, @y)) },
        array_comp => sub { my $r = $comp->compare(\(@x, @y)) },
    };
    
    @x = 1 .. 20, map { rand } 1 .. 1_000;
    @y = 1 .. 20, map { rand } 1 .. 1_000;
    
    cmpthese -2, {
        iterator => sub { my $r = elementwise_eq(\(@x, @y)) },
        my_comp => sub { my $r = my_comp(\(@x, @y)) },
        array_comp => sub { my $r = $comp->compare(\(@x, @y)) },
    };
    
    sub elementwise_eq {
        my ($xref, $yref) = @_;
        return unless  @$xref == @$yref;
    
        my $it = each_arrayref($xref, $yref);
        while ( my ($x, $y) = $it->() ) {
            return unless $x eq $y;
        }
        return 1;
    }
    
    sub my_comp {
        my ($xref, $yref) = @_;
        return unless  @$xref == @$yref;
    
        my $i;
        for my $e (@$xref) {
            return unless $e eq $yref->[$i++];
        }
        return 1;
    }
    

    And result in perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi:

                 Rate   iterator array_comp    my_comp
    iterator   1544/s         --       -67%       -80%
    array_comp 4697/s       204%         --       -41%
    my_comp    7914/s       413%        68%         --
                   Rate   iterator array_comp    my_comp
    iterator    63846/s         --        -1%       -75%
    array_comp  64246/s         1%         --       -75%
    my_comp    252629/s       296%       293%         --
    
    0 讨论(0)
提交回复
热议问题