Анимация
JavaScript
|
Главная Библионтека Комментарий о том, как пользоваться функцпяхп! сравпепня, рассказано в рецепте 4.14. Помимо использования встроенных операторов вроде <=>, можно конструировать более сложные условия: ©ordered = sort { $a->narae cmp $b->name } ©employees, Функция sort часто используется подобным образом в циклах foreach: foreach $егар1оуее (sort {$a->narae cmp $b->name } @>employees) { print $employee->narae, earns \$ , $employee->salary, \n , Если вы собираетесь много работать с элементами, расположенными в определенном порядке, эффективнее будет сразу отсортировать их и работать с отсортированным списком: @sorted employees = sort { $a->name cmp $b->name } ©employees, foreach Seraployee (©sorted employees) { print $employee->name, earns \$ , $employee->salary, \n , tl Загрузить %bonus foreach Seraployee (©sorted employees) ( If ($bonus( $employee->ssn } ) { print $employee->name, got a bonus\n , В функцию можно включить несколько условий и разделить их операторами , Оператор И возвращает первое истшпюе (ненулевое) значение Следовательно, сортировку можно выполнять гю одному критерию, а при равенстве элементов (когда возвращаемое значение равно 0) сортировать гю другому критерию. Получается «сортировка внутри сортировки»: ©sorted = sort ($a->name cmp $b->name $b->age <=> Sa->age} employees, ПервыГг критерий сравнивает имена двух работников. Еслн они не совпадают, 11 прекращает вычисления и возвращает результат cmp (сортировка в порядке возрастания имей). Но если имена совпадают, 11 продолжает проверку и возвращает результат <=> (сортировка в порядке убывания возраста). Полученный список будет отсортирован по именам и но возрасту в группах с одинаковыми именами. Давайте рассмотрим реальный пример сортировки. Мы собираем информацию обо всех пользователям в виде объектов User pwent, после чего сортируем их по и.менам и выводим отсортированный список: use User pwent qw(getpwent), ©users =0, # Выбрать всех пользователей while (aefined($user = getpwent)) { push(©users, Suser), ©users = sort { $a->name crap $b-<narae } ©users, foreach $user (©users) ( print $user->name, \n , Возможности не ограничиваются простыми сравнениями или комбинациями простых сравнений. В следующем примере список имен сортируется по второй букве имени. Вторая буква извлекается функцией substr: ©sorted = sort { substr($a 1,1) cmp substr($b,1 1) } ©names, A ниже список сортируется по длине строки: ©sorted = sort ( length $а <=> length $b } ©strings, Функция сравнения вызывается sort каждый раз, когда требуется сравнить два элемента. Число сравнений заметно увеличивается с количеством сортируемых элементов. Сортировка 10 элементов требует (в среднем) 46 сравнений, однако при сортировке 1000 элементов выполняется 14 000 сравнений. Медленные операции (например, split или вызов подпрограммы) при каждом сравиеиии тормозят работу программы. К счастью, проблема решается однократным выполнением операции для каждого элемента перед сортировкой. Воспользуйтесь тар для сохрапеиия результатов операции в массиве, элементы которого являются анонимными массивами с исходным и вычисленным полем. Этот «массив массивов» сортируется по предварительно вычисленному полю, после чего тар используется для получения отсортированных исходных данных. Концепция map/sort/map применяется часто и с пользой, поэтому ее стоит рассмотреть более подробно. Применим ее к примеру с сортировкой по длине строки: ©temp = map ( [ length $ , $ ] } ©strings, ©temp = sort ( $a->[0] <=> $b->[0] } ©temp, ©sorted = map ( $ ->[1] } ©terap, В первой строке map создает временный массив строк с их длинами. Вторая строка сортирует временный массив, сравнивая их предварительно вычисленные длины. Третья строка превращает временный массив строк/длин в отсортированный массив строк. Таким образом, длина каждой строки вычисляется всего один раз. Поскольку входные данные каждой строки представляют собой выходные данные предыдущей строки (массив @temp, созданный в строке 1, передается sort в строке 2, а результат сортировки передается тар в строке 3), их можно объединить в одну команду и отказаться от временного массива: ©sorted = map ( $ ->[1] } sort ($а->[0] <=> $b->[0] } map { [ length $ , $ ] } ©strings. Теперь операции перечисляются в обратном порядке. Встречая конструкцию map/sort/map, чита11те ее снизу вверх: @st rings: в конце указываются сортируемые данные. В данном случае это массив, но как вы вскоре убедитесь, это может быть вызов подпрограммы или даже команда в обратных апострофах. Подходит все, что возвращает список для последующей сортировки. тар: нижний вызов тар строит временный список анонимных массивов. Список содержит нары из предварительно вычисленного поля (length $ ) и исходного элемента ($ ). В этой строке показано, как происходит вычисление поля. sort: список анонимных массивов сортируется посредством сравнения предварительно вычисленных полей. По этой строке трудно о чем-то судить - разве что о том, будет ли список отсортирован в порядке возрастания или убывания. тар: вызов шар в начале команды превращает отсортированный список анонимных массивов в список исходных отсортированных элементов. Как правило, во всех конструкциях map/sort/map он выглядит одинаково. Ниже показан более сложный пример, в котором сортировка выполняется по первому числу, найденному в каждой строке (Sif lelds: @temp = map { [ /(\d+)/, $ ] } ©fields, @sorted temp = sort {$a->[0] <=> $b->[0] } @temp, @sorted fields = map { $ ->[1] } @sorted temp. Регулярное выражение в первой строке извлекает из строки, обрабатываемой тар, первое число. Мы используем регулярное выражение /(\d+)/ в списковом контексте. Из этого фрагмента можно убрать временный массив. Код принимает следующий вид: @sorted fields = map { $ ->[1] } sort { $а->[0] <=> $b->[0] } map { [ /(\d+)/, $ ] } ©fields, В последнем примере выполняется компактная сортировка данных, разделенных запятыми (они взяты из файла UNIX passwd). Сначала выполняется числовая сортировка файла по четвертому полю (идентификатору группы), затем - числовая сортировка по третьему полю (идентификатору пользователя) и алфавитная сортировка по первому полю (имени пользователя). print map { $ ->[0] } # Целая строка sort { $а->[1] <=> $Ь->[1] # Идентификатор группы II $а->[2] <=> $Ь->[2] # Идентификатор пользователя II $а->[3] <=> $b->[3] # Имя пользователя тар { [ $ , (split / /)[3,2,0] ] } cat /etc/passwd, 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [ 41 ] 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |