2

私はPerlの世界に不慣れで、2つの配列を比較するスクリプトがあります。

比較にはList::MoreUtils( )を使用します。each_arrayref

2つの質問があります:

1)each_arrayrefのように一度に単一の要素を比較する代わりに、配列の2つのチャンクを比較する方法はありますか(natatimeのようですが2つのarrayrefの場合)?

要素は、各配列の同じインデックスからのものである必要があります。

データ構造は次のようなものです。

{
  atr => [qw/ a b c d /],
  ats => [qw/ a b c d /],
  att => [qw/ a b c d /],
}

これは私がこれまでに得たものです。

my @lists = keys %{$hash};

for (my $i = 0; $i <= @lists; $i++) {

  my $list_one = $lists[$i];
  my $one = $hash->{$list_one};

  for (my $j = 0 ; $j <= @lists ; $j++) {

    my $list_two = $lists[$j];
    my $two = $hash->{$list_two};

    my ($overlapping, $mismatch, $identity);
    my $match          = 0;
    my $non_match      = 0;
    my $count_ac_calls = 0;
    my $each_array     = each_arrayref($one, $two);

    while (my ($call_one, $call_two) = $each_array->()) {

      if ((defined $call_one) && (defined $call_two)) {
        if ($call_one eq $call_two) {
          $match++;
        }
        if ($call_one ne $call_two) {
          $non_match++;
        }
      }
    }    #end of while loop $each_array->()

    print "$list_one,$list_two,$match,$non_match";

  }    #end of for j loop
}    #end of for i loop

atr-> ats、atr-> att、ats->attを比較したいと思います。しかし、現在のコードでは、ats-> atr att-> atr、att->atsのように比較が繰り返されます。

2)どうすればそれらを回避できますか?

4

3 に答える 3

4

あなたの最初の質問が何を意味するのかわかりません。(('a','b','c'),('a','b','c')) たとえば、代わりに 戻るイテレータが必要です('a','a')か?もしそうなら、ライブラリに利用できるものはありませんが、自分で書くのは難しいことではありません。

2番目の場合、アイテムがそれ自体と比較されないようにする通常の方法は、最初の値の現在の値の後に開始するように内部ループを変更することです。そのようです

for my $i (0..$#lists) {

  for my $j ($i+1..$#lists) {

  }

}

これA eq Bは、一般的にはと同じB eq Aであるため機能します。したがって、逆比較がすでに行われているため、エントリをリストの前のエントリと比較しても意味がありません。

for厄介なCスタイルの構文よりも、この方法でループを書く方がはるかに優れていることに注意してください。また、いくつかのバグがあります

for (my $i = 0 ; $i <= @lists ; $i++) { ... }

の最大インデックスは@listsのスカラー値より1小さいため、@lists通常はとしてコード化され$#listsます。同じ問題がのループにも存在します$j

アップデート

これがあなたのプログラムのリファクタリングであり、私が説明したアイデアを含み、よりパーリッシュになるように書かれています。お役に立てば幸いです。

use strict;
use warnings;

use List::MoreUtils 'each_arrayref';

my $hash = {
  atr => [qw/ a b c d /],
  ats => [qw/ a b c d /],
  att => [qw/ a b c d /],
};

my @keys = keys %{$hash};

for my $i (0 .. $#keys) {

  my $key1 = $keys[$i];
  my $list1 = $hash->{$key1};

  for my $j ($i+1 .. $#keys) {

    my $key2 = $keys[$j];
    my $list2 = $hash->{$key2};

    my ($match, $non_match) = (0, 0);
    my $iter = each_arrayref($list1, $list2);

    while (my ($call1, $call2) = $iter->()) {
      if (defined $call1 and defined $call2) {
        ($call1 eq $call2 ? $match : $non_match)++;
      }
    }

    print "$key1, $key2, $match, $non_match\n";
  }
}
于 2013-01-08T16:44:20.520 に答える
1

1つのオプションは、Array :: Compareを使用して、さまざまな配列要素の数を返すことです。また、Math :: Combinatoricsは、一意の比較のみを取得するために使用されます。

use strict;
use warnings;
use Array::Compare;
use Math::Combinatorics;

my %hash = (
    'atr' => [ 'a', 'b', 'c', 'd' ],
    'ats' => [ 'a', 'b', 'c', 'd' ],
    'att' => [ 'a', 'c', 'c', 'd' ],
);

my $comp = Array::Compare->new( DefFull => 1 );
my $combinat = Math::Combinatorics->new(
    count => 2,
    data  => [ keys %hash ],
);

while ( my ($key1, $key2) = $combinat->next_combination ) {
    my $diff = $comp->compare( \@{ $hash{$key1} }, \@{ $hash{$key2} } );
    print "$key1,$key2," . ( @{ $hash{$key1} } - $diff ) . ",$diff\n";
}

出力:

ats,att,3,1
ats,atr,4,0
att,atr,3,1
于 2013-01-08T18:41:31.207 に答える
-1

あなたはPerlが提供しなければならない機能を実際に利用していません。エラーが発生しやすいCスタイルのループを使用するのではなく、を使用してくださいfor my $var (LIST)。セルフチェックをスキップすることで、冗長なリストチェックをスキップすることもできます。私はあなたのスクリプトを取り、いくつかの変更を加えました、そしてあなたはそれがもう少し読みやすいと思うでしょう。

use v5.16;
use warnings;
use List::MoreUtils qw{each_arrayref};

my $hash = {
  'atr' => [
    'a',
    'b',
    'c',
    'd'
   ],
  'ats'=>[
    'a',
    'b',
    'c',
    'd'
   ],
  'att' => [
    'a',
    'c',
    'c',
    'd'
   ],
};

for my $list_one (keys $hash) {
    my $one = $hash->{$list_one};

    for my $list_two (keys $hash) {
        next if $list_one ~~ $list_two;

        my $two = $hash->{$list_two};

        my ($match, $non_match);
        $match = $non_match = 0;

        my $each_array = each_arrayref($one, $two);
        while (my ($call_one, $call_two) = $each_array->()) {
            if($call_one && $call_two) {
                if($call_one eq $call_two) {
                    $match++;
                }
                else {
                    $non_match++;
                }
            }
        }

        print "$list_one,$list_two,$match,$non_match\n";
    }
}

とにかく一度に1つずつ評価して、インデックスの場所などのビットを追加できるようにします。(はい、Cスタイルのループを使用することもできますが、それは少し読みにくいでしょう。)

于 2013-01-08T18:37:16.637 に答える