2012-01-09 3 views
1

У меня есть два массива:Perl Несовпадение между массивами

@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"; 

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

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

+0

Вы не хотите использовать один и тот же показатель для каждого из них? –

+0

Извините, я не понимаю ваш вопрос. –

ответ

1

Вот пример использования 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"; 
} 
+0

Привет всем. Я использовал Array :: Каждый модуль, чтобы сравнить тот же индекс. И это тоже сработало. Спасибо каждому за свое время. Я также узнал много других новых способов. –

+0

@SipraMoon [Array :: Every] (http://p3rl.org/Array::Each) будет заметно медленнее, чем [each_arrayref] (http://p3rl.org/List::MoreUtils) (при условии, что что XS был скомпилирован). –

1

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

@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

ОП заданные массивы могут быть разных размеров. Вы просто игнорируете случай, когда array2 больше, чем array1. – derobert

+0

Это правда, и я собирался добавить это как предостережение. –

0

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

foreach my $a1 (0 .. $#array1) { 
    if($array1[$a1] ne $array2[$a1]) { 
    print "$a1: $array1[$a1] <-> $array2[$a1]\n"; 
    } 
} 
+0

ОП заданные массивы могут быть разных размеров. Вы просто игнорируете случай, когда array2 больше, чем array1. – derobert

1

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

@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 
+0

Это не будет работать так хорошо, если массивы имеют разную длину, а некоторые из них равны undef (например, пустая строка). Или, если a2 больше, чем a2 (undef-эквивалент или нет). ОП задает разные длины. – derobert

+0

@derobert: Я исправил свой ответ на вопрос о длине. Однако я не понимаю вашу мысль об undef; OP не указывал такую ​​возможность, и исходный код работал нормально, если я установил один из элементов списка в пустую строку. Конечно, вы не можете сделать это в списке, но я не рассматривал эту часть проблемы. –

+0

Во-первых, я исправил опечатку в вашем тернарном операторе. Во-вторых, проблема undef рассматривает массивы '@ a1 = ('a', undef, 'c'); @ a2 = ('a', '', 'c', '') '. Ваш код говорит, что нет несоответствий; но я думаю, что кто-то ожидал бы двух несоответствий. Perl считает, что '' 'eq undef' является истинным. Undef в середине может быть понятным (именно так определяется eq), но лишний элемент пустой строки, который остается незамеченным, является удивительным для пользователя вашего кода, я думаю. – derobert

1

Ну, во-первых, вы будете хотеть, чтобы идти по каждому элементу одного из массивов, и сравнить его с тем же элементом другого массива. Список :: 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); 
4

Вы не должны иметь вложенные циклы. Вам нужно только пройти через индексы один раз.

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 " . ([email protected]) . " 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 " . ([email protected]) . " mismatches"; 
for my $i (@mismatches) { 
    ... 
} 
+0

@ Сипра Мун, я пропустил, что вы хотите фактическое несоответствие. Исправлена. – ikegami

Смежные вопросы