Почему эти разные функции сортировки Perl дают мне разные порядки элементов? - PullRequest
0 голосов
/ 18 ноября 2009

Я скопировал этот пример из perldoc -f sort . Я добавил массив @old и отпечатки массивов @new. Почему я получаю три разных @new массива? Что-то не так с моим @old?

@old = qw( =332 =43 =avxc =aed =jjj =3322 =aa44 =ssss );
say "\nold  :   @old\n";

# inefficiently sort by descending numeric compare using
# the first integer after the first = sign, or the
# whole record case-insensitively otherwise
@new = sort {
($b =~ /=(\d+)/)[0] <=> ($a =~ /=(\d+)/)[0]
||
uc($a) cmp uc($b)
} @old;
say "new_1:   @new"; # =3322 =332 =43 =aa44 =aed =avxc =jjj =ssss

# same thing, but much more efficiently;
# we'll build auxiliary indices instead
# for speed
@nums = @caps = ();
for (@old) {
push @nums, /=(\d+)/;
push @caps, uc($_);
}
@new = @old[ sort {
$nums[$b] <=> $nums[$a]
||
$caps[$a] cmp $caps[$b]
} 0..$#old
];
say "new_2:   @new"; # =avxc =332 =43 =3322 =aa44 =aed =jjj =ssss

# same thing, but without any temps
@new = map { $_->[0] }
sort { $b->[1] <=> $a->[1]
||
$a->[2] cmp $b->[2]
} map { [$_, /=(\d+)/, uc($_)] } @old;
say "new_3:   @new\n"; # =3322 =332 =43 =avxc =aed =jjj =aa44 =ssss

Ответы [ 2 ]

4 голосов
/ 18 ноября 2009

Пустые списки

Три списка отличаются тем, что регулярное выражение

/=(\d+)/

может вернуть пустой список (если он не совпадает) --- испортить ваши структуры. Я вставил в ваш код следующие строки и получил указанные ответы:

say "nums: @nums";  # nums: 332 43 3322
say "caps: @caps";  # caps: =332 =43 =AVXC =AED =JJJ =3322 =AA44 =SSSS

Видите ли, любой элемент, который не соответствует регулярному выражению, отсутствует в @nums и портит ваш список. Это можно исправить, изменив определение @nums на:

push @nums, /=(\d+)/? $1:undef;

( Редактировать: добавлено уточнение) Та же проблема возникает в вашем последнем примере:

For $_="=123", [$_, /=(\d+)/, uc($_)] = ["123",  123,  123].
For $_="abcd", [$_, /=(\d+)/, uc($_)] = ["abcd", "ABCD"].

Совпадение исчезает, и строка в верхнем регистре перемещается на свое место. Это не то, что вы хотели. Одним из исправлений является замена регулярного выражения, как указано выше, выражением, которое всегда выдает ровно один скаляр:

[$_, /=(\d+)/? $1:undef, uc($_)]

Другим решением было бы поменять местами два последних элемента в списке:

@new = map { $_->[0] }                                                          
sort { $b->[2] <=> $a->[2]                                                      
||                                                                              
$a->[1] cmp $b->[1]                                                             
} map { [$_, uc($_), /=(\d+)/] } @old;                                          
say "new_3:   @new\n";

Теперь регулярное выражение в конце. Если совпадений нет, список будет коротким (всего два элемента). Тем не менее, $ a -> [2] дает желаемый результат: undef. (Вам может понравиться или не понравиться этот подход, позволяющий кусать ошибку, но иметь правильный результат как побочный эффект чтения perl за пределами более коротких списков).

С этими исправлениями все три списка дают одинаковый результат.

Предупреждения

Пожалуйста, запустите вашу программу с включенным предупреждением "-w". Вы обнаружите, что сравниваете довольно много неопределенных значений и нечисловых значений. Вы должны исправить свою программу, чтобы обойтись без этого, например:

@new = map { $_->[0] }                                                          
sort {                                                                          
defined($b->[2]) && defined($a->[2]) && ($b->[2] <=> $a->[2])                   
||                                                                              
$a->[1] cmp $b->[1]                                                             
} map { [$_, uc($_), /=(\d+)/] } @old;                                          
say "new_4:   @new\n";
1 голос
/ 18 ноября 2009

Это три примера сортировки массива с использованием определяемого пользователем компаратора. Каждый пример выводит отсортированный массив после его запуска.

Поскольку имеется три примера, отсортированный массив печатается три раза. Конечно, вы не должны использовать все три версии, когда хотите отсортировать.

В последнем примере используется преобразование Шварца .

Я думаю, что первоначальный замысел автора состоял в том, чтобы все примеры создавали один и тот же отсортированный вывод, но из-за ошибок, объясненных Yaakov Belch , это не так.

Обратите внимание, что ваши данные не соответствуют ожиданиям компаратора, который вы используете. Используемый вами компаратор ожидает, что все записи будут иметь вид =\d.+ (т. Е. Знак равенства, за которым следует цифра, за которой следуют произвольные символы). Если все записи не соответствуют этому формату, вам нужно быть осторожным в компараторе.

Исправлены первый и третий примеры ниже. Я не думаю, что есть необходимость использовать пример 2: Либо стоит предварительно рассчитать, либо нет . Если это так, используйте преобразование Шварца. Если это не так, используйте обычную сортировку, которая извлекает ключи для каждого сравнения.

#!/usr/bin/perl

use 5.010;
use strict; use warnings;

my @old = qw( =332 =43 =avxc =aed =jjj =3322 =aa44 =ssss );

my @new_1 = sort {
    my $ad = $a =~ /=(\d+)/ ? $1 : undef;
    my $bd = $b =~ /=(\d+)/ ? $1 : undef;
    return  1 if defined $bd and not defined $ad;
    return -1 if not defined $bd and defined $ad;
    return $bd <=> $ad if defined $ad and defined $bd;
    return uc $a cmp uc $b;
} @old;

say "new_1:\t@new_1\n"; # =3322 =332 =43 =avxc =aed =jjj =aa44 =ssss

my @new_3 = map { $_->[0] }
    sort {
        return  1 if defined $b->[1] and not defined $a->[1];
        return -1 if not defined $b->[1] and defined $a->[1];
        return  $b->[1] <=> $a->[1] if defined $b->[1] and defined $a->[1];
        return  $a->[2] cmp $b->[2];
} map { [$_, /=(\d+)/ ? $1 : undef, uc($_)] } @old;

say "new_3:\t@new_3\n"; # =3322 =332 =43 =avxc =aed =jjj =aa44 =ssss

Выход:

new_1:  =3322 =332 =43 =aa44 =aed =avxc =jjj =ssss

new_3:  =3322 =332 =43 =aa44 =aed =avxc =jjj =ssss
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...