web-dev-qa-db-ja.com

Perlには、2つの配列が等しいかどうかを比較する組み込みの方法がありますか?

等しいかどうか比較したい2つの文字列の配列があります。

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

スカラーのように配列を比較する組み込みの方法はありますか?私は試した:

if (@array1 == @array2) {...}

しかし、スカラーコンテキストで各配列を評価したため、各配列の長さを比較しました。

私はそれを行うために自分の関数をロールすることができますが、それを行うには組み込みの方法が必要なほど低レベルの操作のようです。ある?

編集:悲しいことに、私は5.10以降またはオプションのコンポーネントにアクセスできません。

50
Bill

新しい スマートマッチ演算子 があります:

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

Array :: Compare :について

内部的に、コンパレータはjoinを使用して両方の配列を文字列に変換し、eqを使用して文字列を比較することにより、2つの配列を比較します。

これは有効な方法だと思いますが、文字列比較を使用している限り、次のようなものを使用したいでしょう。

#!/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;
}

比較する配列が大きい場合、それらを結合すると、各要素を1つずつ比較するよりも多くの作業を行い、多くのメモリを消費します。

Update:もちろん、そのようなステートメントをテストする必要があります。簡単なベンチマーク:

#!/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)) },
};

これは、elementwise_eqは、両方の配列の各要素を1_000回通過する必要があり、次のように表示されます

イテレータの評価array_comp 
 iterator 246/s--75%
 array_comp 1002/s 308%-

一方、最良のシナリオは次のとおりです。

my @x = map { Rand } 1 .. 1_000;
my @y = map { Rand } 1 .. 1_000;
レートarray_compイテレーター
 array_comp 919/s--98%
 iterator 52600/s 5622%-

ただし、iteratorのパフォーマンスは非常に急速に低下します。

my @x = 1 .. 20, map { Rand } 1 .. 1_000;
my @y = 1 .. 20, map { Rand } 1 .. 1_000;
イテレータの評価array_comp 
 iterator 10014/s--23%
 array_comp 13071/s 31%-

私はメモリ使用率を見ませんでした。

56
Sinan Ünür

Test :: More のis_deeply()関数もあります。これは、構造が異なる場所を正確に表示します。 Test :: Deep のeq_deeply()は、テストハーネスが必要です(trueまたはfalseを返すだけです)。

22
Ether

組み込みではありませんが、 Array :: Compare があります。

これは、教訓的な理由だと信じているため、Perlコアから除外されている操作の1つです。つまり、実行しようとしている場合、おそらく何か間違っている可能性があります。これの最もわかりやすい例は、コアの欠如read_entire_file 関数;基本的に、その機能をコアに提供すると、人々はそれを良いアイデアだと思うようになりますが、代わりに、Perlは穏やかに設計されていますファイルを一度に1行ずつ処理するように促します。これは一般にはるかに効率的で、それ以外の場合はより良いアイデアですが、初心者のプログラマーはこれに満足することはめったになく、そこに到達するには何らかの励ましが必要です。

同じことがここでも当てはまります。おそらく、2つの配列を比較することで、達成しようとしている決定を下すためのはるかに良い方法があります。 必ずしも必要ではありませんが、おそらく。ですから、Perlはあなたの目標を達成する他の方法について考えるようにあなたに勧めています。

14
chaos

Perl 5.10は、スマートマッチ演算子を提供します。

use 5.010;

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

さもなければ、あなたが言ったように、あなたはトップロールを自分のものにするでしょう。

9
David Harris

Perl 5.10以降を使用している限り、 スマートマッチ演算子 を使用できます。

if (@array1 ~~ @array2) {...}
8
Quentin

シンプルなソリューションは高速です。

_#!/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;
}
_

そして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%         --
_

この質問は非常に役立つリソースになりました。 ++ベンチマークとディスカッション。

他の人が指摘したように、スマートマッチ機能には問題があり、現在の形で段階的に廃止されています。 「あまり賢くない」(したがって問題を回避する)代替手段があり、それは小さく、かなり高速で、あまり多くの非CORE依存関係を持たないものです。

~~の将来の歴史に関する非常に優れた議論へのリンクは、いくつかの ブログ投稿 by @brian d foyとp5pメールアーカイブスレッド 2011年から および@ -rjbsから 2012

配列の比較は簡単で楽しいことができます!

use v5.20;   
use match::smart; 
my @x = (1, 2, 3);       
my @y = qw(4 5 6);    
my @z = qw(4 5 6);   
say \@x |M| \@y ? "[\@x] and [\@y] match": "no match";  
say \@y |M| \@z ? "[\@y] and [\@z] match": "no match";

__END__                              
@y and @z match, @x and @y do not

...配列が単純な場合は特に楽しいです。しかし、配列は複雑なものになる可能性があり、比較の結果からさまざまな種類の情報が必要になる場合があります。そのため、 Array :: Compare は微調整された比較をより簡単にすることができます。

2
G. Cito

_Data::Cmp_ は別の最近のオプションです。 cmp_data()関数は、cmp演算子と同様に動作します(perlopの使用法については cmp を参照してください)。

例:

_use 5.10;
use Data::Cmp qw/cmp_data/;

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

# sample usage 
say "1 & 2 are different" if cmp_data(\@array1, \@array2) ;
sat "2 & 3 are the same" unless cmp_data(\@array2, \@array3) ;
_

ハッシュとより複雑なネストされたデータ構造を比較することも可能です(理由の範囲内で)。コア以外の依存関係がない単一のモジュールの場合、_Data::Cmp_はかなり「スマート」です;-)... errm私は「役に立つ」 「。

1
G. Cito

大文字と小文字が唯一の違いである場合は、次を使用できます。

_if (lc "@array1" eq lc "@array2") {...}
_

一方、_"@array1"_はjoin ( " ", @array1 )と同じを返します

1
tempire

2つの配列の等価性を確認するには、これを試してください。指定されたコードでは、%eq_or_notに値がある場合、両方の配列は等しくありません。そうでない場合は等しくなります。

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

my %eq_or_not;

@eq_or_not{ @array1 } = undef;
delete @eq_or_not{ @array2 };
1
Pradeep Gupta

値の順序と重複は問題ではなく、値の等価性のみを設定する(つまり、比較を設定する)場合、 Set::Scalar を使用できます。

==!=などの一般的な演算子をオーバーロードします。

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

if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}

または、 Algorithm::DiffList::Compare もあります。

1
Archimedix

スカラーコンテキストでgrep関数を使用できます( http://perldoc.Perl.org/functions/grep.html#grep-BLOCK-LIST

(0 eq(grep {$ array1 [$ _] ne $ array2 [$ _]} 0 .. $#array1))if $#array1 eq $#array2;

こんにちは。

0
f_v