Perl Несоответствие среди массивов - PullRequest
1 голос
/ 10 января 2012

У меня есть два массива:

@array1 = (A,B,C,D,E,F);
@array2 = (A,C,H,D,E,G);

Массивы могут быть разных размеров.Я хочу узнать, сколько существует несоответствий между массивами.Индексы должны быть одинаковыми.В этом случае есть три несоответствия: b->c, c->h и F->G. (Т. Е. 'C' в $array[2] не следует считать совпадением с 'C' в $array[1])Я хотел бы получить количество несоответствий, а также несоответствие.

foreach my $a1 ( 0 .. $#array1) {
 foreach my $a2( 0 .. $#array2)
  if($array1[$a1] ne $array2[$a2]) {

   }
 }
}

my %array_one = map {$_, 1} @array1;
my @difference = grep {!$array_one {$_}} @array1;

print "@difference\n";

Ответ: дает мне H, G, но не C.

с моим маленькимЗнание Perl Я попробовал это, без результата.Не могли бы вы предложить мне, как я должен это делать?Ваши предложения и указатели будут очень полезны.

Ответы [ 6 ]

4 голосов
/ 10 января 2012

У вас не должно быть вложенных циклов.Вам нужно всего лишь один раз пройтись по индексам.

use List::Util qw( max );

my @mismatches;
for my $i (0..max($#array1, $#array2)) {
   push @mismatches, $i
      if $i >= @array1
      || $i >= @array2
      || $array1[$i] ne $array2[$i];
   }
}

say "There are " . (0+@mismatches) . " mismatches";
for my $i (@mismatches) {
   ...
}

Поскольку вы упомянули grep, вы должны заменить for на grep:

use List::Util qw( max );

my @mismatches =
    grep {  $_ >= @array1
         || $_ >= @array2
         || array1[$_] ne $array2[$_] }
    0 .. max($#array1, $#array2);

say "There are " . (0+@mismatches) . " mismatches";
for my $i (@mismatches) {
   ...
}
1 голос
/ 10 января 2012

Вот пример использования each_arrayref из List :: MoreUtils .

sub diff_array{
  use List::MoreUtils qw'each_arrayref';
  return unless @_ && defined wantarray;
  my @out;

  my $iter = each_arrayref(@_);

  my $index = 0;
  while( my @current = $iter->() ){
    next if all_same(@current);

    unshift @current, $index;
    push @out, \@current;
  }continue{ ++$index }

  return @out;
}

Эта версия должна быть быстрее, если вы собираетесь использовать ее для определения количества различий часто. Выход точно такой же. Просто не нужно так усердно работать при возврате числа.
Подробнее о wantarray.

sub diff_array{
  use List::MoreUtils qw'each_arrayref';
  return unless @_ && defined wantarray;

  my $iter = each_arrayref(@_);

  if( wantarray ){
    # return structure
    my @out;

    my $index = 0;
    while( my @current = $iter->() ){
      next if all_same(@current);

      unshift @current, $index;
      push @out, \@current;
    }continue{ ++$index }

    return @out;

  }else{
    # only return a count of differences
    my $out = 0;
    while( my @current = $iter->() ){
      ++$out unless all_same @current;
    }
    return $out;
  }
}

diff_array использует подпрограмму all_same, чтобы определить, совпадают ли все текущие списки элементов.

sub all_same{
  my $head = shift;
  return undef unless @_; # not enough arguments
  for( @_ ){
    return 0 if $_ ne $head; # at least one mismatch
  }
  return 1; # all are the same
}

Чтобы получить только количество различий:

print scalar diff_array \@array1, \@array2;
my $count  = diff_array \@array1, \@array2;

Чтобы получить список отличий:

my @list = diff_array \@array1, \@array2;

Чтобы получить оба:

my $count = my @list = diff_array \@array1, \@array2;

Выход для введенного вами ввода:

(
  [ 1, 'B', 'C' ],
  [ 2, 'C', 'H' ],
  [ 5, 'F', 'G' ]
)

Пример использования

my @a1 = qw'A B C D E F';
my @a2 = qw'A C H D E G';

my $count = my @list = diff_array \@a1, \@a2;

print "There were $count differences\n\n";

for my $group (@list){
  my $index = shift @$group;
  print "  At index $index\n";
  print "    $_\n" for @$group;
  print "\n";
}
1 голос
/ 10 января 2012

Ну, во-первых, вы захотите просмотреть каждый элемент одного из массивов и сравнить его с тем же элементом другого массива. List :: MoreUtils предоставляет простой способ сделать это:

use v5.14;
use List::MoreUtils qw(each_array);

my @a = qw(a b c d);
my @b = qw(1 2 3);

my $ea = each_array @a, @b;
while ( my ($a, $b) = $ea->() ) {
    say "a = $a, b = $b, idx = ", $ea->('index');
}

Вы можете расширить это, чтобы найти несоответствие, проверив внутри цикла while (примечание: предполагается, что у ваших массивов нет undefs в конце, или что если они есть, undef - это то же самое, что и более короткий массив):

my @mismatch;
my $ea = each_array @a, @b;
while ( my ($a, $b) = $ea->() ) {
    if (defined $a != defined $b || $a ne $b) {
        push @mismatch, $ea->('index');
    }
}

и затем:

say "Mismatched count = ", scalar(@mismatch), " items are: ", join(q{, }, @mismatch);
1 голос
/ 10 января 2012

Следующий код создает список несовпадающих пар, а затем распечатывает их.

@a1 = (A,B,C,D,E,F);
@a2 = (A,C,H,D,E,G);
@diff = map { [$a1[$_] => $a2[$_]] }
            grep { $a1[$_] ne $a2[$_] }
                 (0..($#a1 < $#a2 ? $#a1 : $#a2));
print "$_->[0]->$_->[1]\n" for @diff
1 голос
/ 10 января 2012

Вы перебираете оба массива, когда не хотите этого делать.

@array1 = ("A","B","C","D","E","F");
@array2 = ("A","C","H","D","E","G");
foreach my $index (0 .. $#array1) {
   if ($array1[$index] ne $array2[$index]) {
       print "Arrays differ at index $index: $array1[$index] and $array2[$index]\n";
   }
}

Выход:

Arrays differ at index 1: B and C
Arrays differ at index 2: C and H
Arrays differ at index 5: F and G
0 голосов
/ 10 января 2012

У вас правильная идея, но вам нужен только один цикл, поскольку вы просматриваете каждый индекс и сравниваете записи между массивами:

foreach my $a1 ( 0 .. $#array1) {
  if($array1[$a1] ne $array2[$a1]) {
     print "$a1: $array1[$a1] <-> $array2[$a1]\n";
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...