Я хочу отсортировать массив массивов в Perl, но результат не отсортирован - PullRequest
14 голосов
/ 13 января 2012

У меня есть массив массивов, которые я хочу отсортировать. Каждый элемент массива A представляет собой массив из 3 элементов. Массив А выглядит так:

my @A = ([2,3,1], [1,2,3], [1,0,2], [3,1,2], [2,2,4]);

Я хочу отсортировать А в порядке возрастания. При сравнении 2 элементов используется первое число. Если есть связь, используется второе число, а затем третье число.

Вот мой код. Я использую функцию 'cmpfunc' для сравнения 2 элементов.

sub cmpfunc {
    return ($a->[0] <=> $b->[0]) or 
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]);
}
my @B = sort cmpfunc @A;
print "Result:\n";
for my $element (@B) {
    print join(",", @{$element}) . "\n";
}

Результат:

1,2,3
1,0,2
2,3,1
2,2,4
3,1,2

Результат несколько отсортирован, но не верен. Я ожидаю, что это:

1,0,2
1,2,3
2,2,4
2,3,1
3,1,2

Есть ли ошибка в моей функции сравнения? Странно то, что когда я помещаю код сравнения в блок, результат корректно сортируется.

my @C = sort { ($a->[0] <=> $b->[0]) or 
               ($a->[1] <=> $b->[1]) or
               ($a->[2] <=> $b->[2]) } @A;

Ответы [ 5 ]

22 голосов
/ 13 января 2012

Вы выполняете

return ($a->[0] <=> $b->[0])

, который возвращается до того, как он попадет в любое из предложений "или".

Либо удалите ключевое слово "return", либо добавьте круглые скобки вокруг весь список аргументов для возврата:

sub cmpfunc {
    return(($a->[0] <=> $b->[0]) or
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]));
}
9 голосов
/ 13 января 2012

Причиной, по которой вы наблюдаете это "неправильное" поведение, является приоритет оператора or, самый низкий из возможных. В этой ситуации это означает, что

return ($a->[0] <=> $b->[0]) or 
       ($a->[1] <=> $b->[1]) or
       ($a->[2] <=> $b->[2]);

интерпретируется как OR-ing

return ($a->[0] <=> $b->[0])

и остальная часть строки - ерунда в этом случае, так как return никогда не возвращается. :)

Так что вы должны использовать C's OR:

return ($a->[0] <=> $b->[0]) || 
       ($a->[1] <=> $b->[1]) ||
       ($a->[2] <=> $b->[2]);
5 голосов
/ 13 января 2012

Требуется больше скобок:

sub cmpfunc {
    return (($a->[0] <=> $b->[0]) or
            ($a->[1] <=> $b->[1]) or
            ($a->[2] <=> $b->[2]));
}
3 голосов
/ 13 января 2012
    sub cmpfunc {
    return ($a->[0] <=> $b->[0]) or 
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]);
}

Вы можете удалить «возврат» здесь.

    sub cmpfunc {
     ($a->[0] <=> $b->[0]) or 
     ($a->[1] <=> $b->[1]) or
     ($a->[2] <=> $b->[2]);
}
2 голосов
/ 13 января 2012

Альтернативное решение Даниэля:

sub cmpfunc {
    return ($a->[0] <=> $b->[0]) ||
           ($a->[1] <=> $b->[1]) ||
           ($a->[2] <=> $b->[2]);
}

Проблема с or в этом случае состоит в том, что он имеет более низкий приоритет, чем присваивание, поэтому ваша функция возвращает только результат ($a->[0] <=> $b->[0]), то есть -1, 0 или 1, если левая сторона численно ниже, равна или больше, чем правая сторона соответственно.|| имеет более высокий приоритет, поэтому перед возвращением вычисляется все логическое выражение.Как уже упоминалось, вы можете заключить выражение в скобки, если вы предпочитаете это ||.Я лично не.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...