Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > как отсортировать такой ряд?


Автор: zyxerr 23.7.2008, 21:24
вопрос модератору - у меня много разнотипных вопросов, мне лучше сразу
их запостить в одной ветке или создавать новую для каждого вопроса?
.
суть дела:
мне нужно отсортировать файлы в таком порядке:
.
10000000_1.gif
10000001_1.gif
10000001_2.gif
10000002_01.gif
10000002_02.gif
10000002_03.gif
10000002_04.gif
10000002_05.gif
10000002_06.gif
10000002_07.gif
10000002_08.gif
10000002_09.gif
10000002_10.gif
10000003_1.gif
10000004_1.gif
.
где 10000000 - это номер документа, _1 - номер страницы.
Если страниц больше 10, то номера начинаются с нуля,
если больше 100 - с двух нулей (_001, _002)
.
если применить обычную числовую сортировку ($a<=>$b),
то получается такой порядок: _58, _59, _6, _60, _61,
то есть _6 стоит не после _5, а после _59
.
я нашел пример в мануале, где разбиралась сложная
сортировка и методом тыка написал вот такую функцию

Код

@gif=sort{($a=~ /(\d+)_/)[0] <=> ($b=~ /(\d+)_/)[0]  
                             ||
     ($a=~ /_(\d+)/)[0] <=> ($b=~ /_(\d+)/)[0]}@unsorted

и она работает! но я так и не понял - зачем в ней такая [0] штука? 
если удалить, то не работает..


Автор: amg 24.7.2008, 07:33
Цитата(zyxerr @  23.7.2008,  21:24 Найти цитируемый пост)
и она работает! но я так и не понял - зачем в ней такая [0] штука? если удалить, то не работает..
То что работает -- не удивительно, т.к. все правильно. Молодец! 
Зачем [0]? Дело в том, что результат работы регулярного выражения, например, $a =~ /(\d+)_/, зависит от контекста, последний бывает скалярный либо списковый. Оператор <=> требует скаляр, и регулярное выражение вернет ему 1 (есть совпадение) либо 0 (нет). Конструкция же ()[0] (первый элемент списка) потребует от регулярного выражения список. А в списковом контексте регулярное выражение возвращает элементы, захваченные скобками ((\d+), цифры перед подчерком, в данном случае список будет состоять только из одного элемента).

Еще немного про сортировку. В перле сортировка хоть и быстрая, но все же будет много сравнений (в среднем N*ln(N), где N -- количество элементов массива). В твоей сортировке на каждое сравнение нужно оценить от 2 до 4 регулярных выражений. Оценка регулярного выражение -- не самая быстрая операция, и если массив большой, будет задержка.

Немного модифицированная функция несколько улучшит ситуацию, в ней ровно 2 регулярных выражения на каждое сравнение
Код
@gif =sort {my ($a1,$a2) = $a=~/(\d+)_(\d+)/;
            my ($b1,$b2) = $b=~/(\d+)_(\d+)/;
            $a1 <=> $b1 || $a2 <=> $b2 } @gif;


Еще лучше оценку регулярных выражений провести заранее и только 1 раз для каждого элемента массива. Это делается примерно так:
Код
@gif = map {$_->[0]}
       sort {$a->[1] <=> $b->[1] || $a->[2] <=> $b->[2]}
       map {[$_,/(\d+)_(\d+)/]} @gif;
Подобный прием просто необходим, если нужно не просто оценить регулярное выражение (все же это довольно быстро), а выполнить какое-либо сложное медленное преобразование.


И, наконец, про разнотипные вопросы -- постить их в одной ветке или создавать новую для каждого вопроса. Хоть я и не модератор, но, очевидно, следует создавать новую ветку для каждого вопроса.

Автор: zyxerr 24.7.2008, 18:50
спасибо! все таки Perl - удивительная штука smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)