Все для вебмастера!!!



 
Обзор сайта

JavaApplet

Images

    Фоны

    Банеры

    Gif-ы

    Кнопки
Шрифты         
Ссылки           
Русские доки
    Perl
Форум            
Отзывы          
Рассылка

Рассылки Subscribe.Ru
Все для вебмастеров. Куча картинок, скриптов, доков.
Рассылка 'Все для вебмастеров. Куча картинок, скриптов, доков.'

PERL - Полезные советы.

Работа с массивами.

Иногда бывает необходимо преобразовать массив чего-либо в хэш-массив. Это можно сделать так:


        %hash = map { $_, 1 } @array;
Например:

        %hash = map { $_, 1 } qw(a b c);
Этот оператор преобразует массив ('a', 'b', 'c') в хэш-массив ('a', 1', 'b', 1, 'c', 1).

Автор: Joseph N. Hall

Предположим, мы имеем http log примерно следующего содержания

gwa.fr.bosch.de - - [08/Jan/1998:01:50:42 -0700] "GET /ack.html HTTP/1.0" 200 6798
fwigka.admin.ch - - [08/Jan/1998:01:53:21 -0700] "GET /toc.html HTTP/1.0" 200 10002
и нам хочется сделать хэш-массив, состоящий из первых слова каждой строки, а значением элемента массива - количество повторений этого слова

        @host{/^(\S+)/}++ while <>;
Этот же фрагмент кода можно переписать и в более пространном виде

        while (<>) {          # Считываем одну строку в переменную $_
                my ($addr) = /^(\S+)/;  # Получаем первое слово строки
                $host{$addr}++;         # Увеличиваем счетчик повторений слова
        }
Вот еще подобный фрагмент кода

        %host = map {/^(\S+)/, 1} <>;
Но в этом случае содержимое всего файла считывается в память (<> в контексте списка) перед тем, как продолжить вычисления. Это, конечно, неудобно в случае файла большого размера.

Автор: Joseph N. Hall

Инверсия хэш-массива один-к-одному.
Предположим, мы имеем следующий хэш-массив, содержащий информацию о соответствии IP адресов и символьных имен

        %num_to_host = (
                '123.234.1.1' => 'george',
                '123.234.1.2' => 'jane',
                '123.234.1.3' => 'judy'
        );
Таким образом, используя IP адрес в качестве индекса мы можем получить его символьное имя. А как сделать наоборот?

        %host_to_num = reverse %num_to_host;
В случае хэш-массива с отношением один-к-одному мы получим хэш-массив с обратным соответствием, т.е. сможем определять IP адрес имея его символьное имя.

        ('george' => '123.123.1.1',
        'judy' => '123.234.1.3',
        'jane' => '123.234.1.2')

Автор: Joseph N. Hall

Поиск.

Проверка: если ли в файле обе искомые строки.

Нам необходимо определить, если ли в файле обе строки текста 'george' and 'judy'?


        my ($s1, $s2);
        while (<>) {
                exit 0 if ($s1 ||= /george/) & ($s2 ||= /judy/);
        }
        die "not found\n";
Соль этого фрагмента в использовании операторов ||= (ИЛИ-присвоение) и & (побитовый И). Программа считывает файл по строкам в переменную $_ оператором <>. Как только встретится строка 'george', переменной $s1 будет присвоено значение 1 (истина). Не забывайте, что оператор $s1 ||= /george/ означает то же, что и $s1 = $s1 || /george/ -- как только $s1 примет значение ИСТИНА, программа больше не будет делать проверку на строку /george/. $s2 ||= /judy/ работает подобным образом. Программа закончит свое выполнение как только обе переменные $s1 и $s2 получат значение ИСТИНА.

Интересный момент заключается в использовании оператора побитовое И (&) вместо логического И (&&). Левый и правый аргументы оператора & всегда выполняются в отличие от &&. Этот код просто не будет работать, если использовать оператор && и файл содержит строку 'judy' перед строкой 'george'. Конечно, нельзя всегда заменять оператор && на &, но в данном случае это необходимо.

Автор: Joseph N. Hall

Проверка: если ли в файле все необходимые нам слова.

        die "usage: multi string1,string2,string3 [file1 file2 ...]\n"
                unless @ARGV;
         
        my @match;
        for (split /,/, shift) {
                my $regex = "\Q$_\E"; # escape regex chars
                push @match, eval 'sub { $_[0] =~ /$regex/o }';
        }
        my $line;
        while (defined($line = <>)) {
                my @left_to_match;
                for (@match) {
                        push @left_to_match, $_ unless $_->($line);
                }
                exit 0 unless @left_to_match;
                @match = @left_to_match;
        }
        die "not found\n";
Это совершенно другой подход. Мы начинает с того, что создаем анонимные функции, каждая из которых возвращает ИСТИНА, когда ее аргумент совпадает с одним из искомых слов. Ссылки на функции хранятся в массиве @match. Затем, для каждой строки входного файла, мы запускаем по циклу все функции. Если функция не нашла совпадения (искомое слово остутствует) мы сохраняем ссылку на эту функцию в другом массиве @left_to_match - они будут работать над следующими строками исходного файла. Когда все функции сработают, в массиве @left_to_match ничего не останется и программа завершится. В противном случае будет выдано диагностическое сообщение.

Автор: Joseph N. Hall

Сортировка.

Попробуем отсортировать по возрастанию числа от 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

Все вопросы и предложения присылайте мне на E-Mail: webmasteram@mail.ru

Апорт Top 1000 Fair.ru Ярмарка сайтов
Hosted by uCoz