PERL - Полезные советы
Попробуем отсортировать по возрастанию числа от 1 до 10. sort 1..10 дает нам результ ('1', '10', '2', '3', '4', '5', '6', '7', '8', '9'). Немного не то... Сортировка сработала как расстановка по алфавиту. Проблему можно решить с помощью оператора <=>.
@sorted_num = sort { $a <=> $b } 1..10; # То, что мы ожидали
# другой вариант этого же кода
sub numerically { $a <=> $b }
@sorted_num = sort numerically 1..10;
По умолчанию функция сортировки sort выполняет расстановку по алфавиту (сортировка в контексте символьных строк). Таким образом '10' и '100' появятся перед '2' и '3'. Чтобы изменить способ сортировки в данном случае мы применили собственный оператор сравнения двух переменных (блок сортировки).
Автор: Joseph N. Hall
Сортировка одного массива в соответсвии с содержимым другого массива.
Нам надо отсортировать два "параллельных" массива (списка). Например массив @page состоит из номеров страниц, а @note состоит из примечаний к этим страницам, т.е. $note[$i] - это примечание к странице $page[$i]. Нам хочется напечатать оба массива, отсортировав их по номерам страниц.
@page = qw(24 75 41 9);
@note = qw(p.24-text p.75-text p.41-text p.9-text);
for (sort { $note[$a] <=> $note[$b] } 0..$#note) {
print "$page[$_]: $note[$_]\n";
}
# другой вариант
@note_sorted = @note[sort { $page[$a] <=> $page[$b] } 0..$#page];
Автор: Joseph N. Hall
Сортировка по убыванию. Надо просто поменять местами переменные $a и $b в блоке сравнения.
print "descending: ",
join(" ", sort { $b <=> $a } 3,4,1,5,9,7),
"\n";
Автор: Joseph N. Hall
Сортировка ключей хэш-массива в порядке возрастаний их значений. Имеем хэш-массив, состоящий из слов книги и количеством повторений этих слов. Нам надо напечатать этот массив в порядке убывания частоты слова.
# Считываем слова
while (<>) {
for (split) {
$count{$_}++;
}
}
# Теперь выводим список слов и количество повторений
for (sort { $count{$b} <=> $count{$a} } keys %count) {
print "$_: $count{$_}\n";
}
Автор: Joseph N. Hall
Сортировка имен файлов по дате/времени изменения.
@newest_first = sort { -M $a <=> -M $b } <*>;
Оператор -М возвращает дату/время изменения файла в виде числа с плавающей точкой. Проблема состоит в том, что оператор -М выполняется очень медленно и на больших списках файлов операция сортировки может занять очень много времени. Для сортировки n файлов блок сравнения будет вызван примерно n log n раз. Для решения этой проблемы смотрите пример сортировки Шварца.
Автор: Joseph N. Hall
Сортировка величин, время сравнения которых сравнительно велико. Например, нам надо отсортировать файлы по времени последнего изменения, но оператор -М (время последнего изменения файла) работает очень медленно. Решить проблему можно с помощью сортировка Шварца (по имени Рандала Шварца Randal Schwartz).
# Сортировка имен файлов по времени их последнего изменения
@newest_first =
map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, -M ] }
<*>;
# Общая форма. Сортировка по одному ключу
@sorted =
map { $_ ->[0] }
sort { $a->[1] %%compare-op%% $b->[1] }
map { [ $_, %%transform-func%% ] }
@input;
# Общая форма. Сортировка по двум ключам
@sorted =
map { $_ ->[0] }
sort { $a->[1] %%compare-op1%% $b->[1] or
$a->[2] %%compare-op2%% $b->[2] }
map { [ $_, %%transform-func1%%, %%transform-func2%% ] }
@input;
Суть метода заключается в том, что медленные вычисления производятся только один раз, а их результат сохраняется во временном массиве. Дальнейшая сортировка производится над значениями временного массива.
Автор: Joseph N. Hall
Сортировка строк по полям, разделенным символом. Например, хочу отсортировать строки, разделенные на поля запятой, сначала по второму полю по числам, затем по первому полю по алфавиту в порядке убывания.
sub fieldsort {
my ($sep, $cols);
if (ref $_[0]) {
$sep = '\\s+'
} else {
$sep = shift;
}
unless (ref($cols = shift) eq 'ARRAY') {
die "fieldsort columns must be in anon array";
}
my (@sortcode, @col);
my $col = 1;
for (@$cols) {
my ($a, $b) = /^-/ ? qw(b a) : qw(a b);
my $op = /n$/ ? '<=>' : 'cmp';
push @col, (/(\d+)/)[0] - 1;
push @sortcode, "\$${a}->[$col] $op \$${b}->[$col]";
$col++;
}
my $sortfunc = eval "sub { " . join (" or ", @sortcode) . " } ";
my $splitfunc = eval 'sub { (split /$sep/o, $_)[@col] } ';
return
map $_->[0],
sort { $sortfunc->() }
map [$_, $splitfunc->($_)],
@_;
}
#Примеры:
# Как сказано выше
@sorted = fieldsort ':', ['2n', -1], @data;
# по 2-му затем по 1-му полю, по алфавиту, разделены пробелами
@sorted = fieldsort [2, 1], @data;
# по 1-му полю по числам в порядке убывания, затем по 3-му полю
# по алфавиту и по 2-му по числам, поля разделены '+'
@sorted = fieldsort '+', ['-1n', 3, 2], @data;
На самом деле большая часть приведенного выше кода - это препроцессор, который готовит данные для дальнейшей сортировки Шварца.
Автор: Joseph N. Hall
|