Perlには、2つの配列の等価性を比較する組み込みの方法がありますか?
質問
等しいかどうか比較したい2つの文字列の配列があります:
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
スカラーのように配列を比較する組み込みの方法はありますか? 試しました:
if (@array1 == @array2) {...}
ただし、各配列をスカラーコンテキストで評価したため、各配列の長さを比較しました。
それを行うために独自の関数をロールバックすることはできますが、それを行うための組み込みの方法が必要なほど低レベルの操作のようです。ありますか?
編集:残念ながら、5.10以降またはオプションのコンポーネントにアクセスできません。
解決
新しいスマートマッチ演算子:
#!/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;
について
内部的に、コンパレータは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つずつ比較するよりも多くの作業が行われ、多くのメモリが消費されます。
更新:もちろん、そのようなステートメントをテストする必要があります。簡単なベンチマーク:
#!/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 { "新しいスマートマッチ演算子:
#!/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;
について
内部的に、コンパレータは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つずつ比較するよりも多くの作業が行われ、多くのメモリが消費されます。
更新:もちろん、そのようなステートメントをテストする必要があります。簡単なベンチマーク:
Rate iterator array_comp
iterator 246/s -- -75%
array_comp 1002/s 308% --
これは、 elementwise_eq
が両方の配列の各要素を1_000回通過する必要がある最悪のシナリオであり、次のように表示されます。
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% --
my @x = 1 .. 20, map { rand } 1 .. 1_000;
my @y = 1 .. 20, map { rand } 1 .. 1_000;
ただし、 iterator
のパフォーマンスは急速に低下します。
Rate iterator array_comp
iterator 10014/s -- -23%
array_comp 13071/s 31% --
<*>
メモリ使用率を確認しませんでした。
" } 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回通過する必要がある最悪のシナリオであり、次のように表示されます。
一方、最良のシナリオは次のとおりです:
<*> <*> ただし、 iterator
のパフォーマンスは急速に低下します。
メモリ使用率を確認しませんでした。
他のヒント
Test :: More のis_deeply()関数もあります。構造が異なる場所を正確に表示するか、 Test :: Deep のeq_deeply()を表示します。テストハーネスは不要です(trueまたはfalseを返すだけです)。
組み込みではありませんが、 Array :: Compare があります。
これは、教訓的な理由だと私が信じていることから、Perlコアから除外されている操作の1つです。つまり、実行しようとしている場合、おそらく何か問題があります。この最もわかりやすい例は、コアの read_entire_file
関数がないことだと思います。基本的に、その機能をコアに提供すると、人々はそれを行うことを良いアイデアだと思うようになりますが、代わりに、Perlはファイルを1行ずつ優しく処理するように設計されています時間は一般にはるかに効率的で、それ以外の場合はより良いアイデアですが、初心者のプログラマーはそれに慣れることがめったになく、そこに到達するために何らかの励ましが必要です。
ここでも同じことが当てはまります。おそらく、2つの配列を比較して、達成しようとしている決定を下すためのはるかに良い方法があります。 必ずしもではありませんが、おそらくそうです。ですから、Perlはあなたの目標を達成する他の方法について考えるようにあなたに勧めています。
Perl 5.10は、スマートマッチ演算子を提供します。
use 5.010;
if( @array1 ~~ @array2 )
{
say "The arrays are the same";
}
それ以外の場合、あなたが言ったように、あなたはトップロールを自分のものにするでしょう。
perl 5.10以降を使用している限り、スマートマッチ演算子。
if (@array1 ~~ @array2) {...}
よりシンプルなソリューションの方が高速です:
#!/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 { "よりシンプルなソリューションの方が高速です:
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% --
その結果、 perl 5、バージョン14、x86_64-linux-gnu-thread-multi用にビルドされたサブバージョン2(v5.14.2)
:
<*>" } 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、バージョン14、x86_64-linux-gnu-thread-multi用にビルドされたサブバージョン2(v5.14.2)
:
この質問は非常に役立つリソースになりました。 ++ベンチマークとディスカッション。
他の人が指摘したように、スマートマッチ機能には問題があり、現在の形式では段階的に廃止されています。 「あまり賢くない」代替案があります。 (そして問題を回避します)、それは小さく、かなり高速で、あまり多くの非CORE依存関係がありません。
@ brian d foyによるブログ投稿、および p5pメールアーカイブスレッド 2011年からおよび 2012 @rjbsから。
配列の比較は簡単で楽しいことができます!
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 により、微調整された比較を簡単に行うことができます。
大文字と小文字が唯一の違いである場合、単純に使用できます:
if (lc "@array1" eq lc "@array2") {...}
&quot; @ array1&quot;
は、 join(&quot;&quot ;, @ array1)
順序と重複する値は重要ではなく、値の等価性のみを設定する(つまり、比較を設定する)場合は、 Set :: Scalar
。
==
や!=
などの一般的な演算子をオーバーロードします。
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}
また、 Algorithm :: Diff
と List :: Compare
。
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 };
Data :: Cmp
も最近のオプションです。 cmp_data()
関数は、 cmp
演算子と同様に動作します( cmp
を使用する場合は perlop
)。
例:
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私は「有用」という意味です。
スカラーコンテキストでgrep関数を使用できます( http:// perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST )
(0 eq(grep {$ array1 [$ _] ne $ array2 [$ _]} 0 .. $#array1))if $#array1 eq $#array2;
こんにちは。
より複雑な質問ではなく、「同等かどうか」だけが基準である場合、「同等かそうでないか、異なる場合はどのように」?それを行うためのはるかに速い/ ugい方法があります。たとえば、各配列全体を2つのスカラーに粉砕し、それらを比較します。
たとえば
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
my $smash1 = join("", @array1);
my $smash2 = join("", @array2);
if ($smash1 eq $smash2)
{
# equal
}
else
{
#unequal
}
はい、たぶんラリー・ウォールを泣かせただけでしょう。