Глава 3. Операторы

Содержание

Приоритетность
Сравнения и "Умное" сопоставление
Сравнения чисел
Сравнение строк
Three-way сравнение
"Умное" сопоставление

Операторы обеспечивают простой синтаксис для часто используемых действий. Они обладают специальным синтаксисом и позволяют манипулировать значениями.

Вернемся к нашей турнирной таблице из предыдущей главы. Допустим вам потребовалось графически отобразить количество выигранных каждым игроком сетов в турнире. Следующий пример выводит на экран строки из символов X для создания горизонтальной столбчатой диаграммы:

 
        use v6;
 
        my @scores = 'Ana' => 8, 'Dave' => 6, 'Charlie' => 4, 'Beth' => 4;
 
        my $screen-width         = 30;
 
        my $label-area-width = 1 + [max] @scores.key.chars;
        my $max-score            = [max] @scores.value;
        my $unit                 = ($screen-width - $label-area-width) / $max-score;
 
        for @scores {
           my $format = '%- ' ~ $label-area-width ~ "s%s\n";
           printf $format, .key, 'X' x ($unit * .value);
        }
 

На экран будет выведен следующий результат:

 
        Ana         XXXXXXXXXXXXXXXXXXXXXX
        Dave        XXXXXXXXXXXXXXXX
        Charlie     XXXXXXXXXXX
        Beth        XXXXXXXXXXX
 

Строка в примере:

 
        my @scores = 'Ana' => 8, 'Dave' => 6, 'Charlie' => 4, 'Beth' => 4;
 

... содержит три разных оператора: =, =>, и ,.

Оператор = является оператором присваивания . Он берет значения, расположенные справа, и сохраняет их в переменной слева, а именно в переменной @scores.

Как и в других языках, основанных на синтаксисе C, Perl 6 допускает сокращенные формы для записи обычных присвоений. То есть вместо $var = $var op EXPR использовать $var op= EXPR. Например, ~ (тильда) - оператор строковой конкатенации (объединения); для добавления текста к концу строки достаточно выражения $string ~= "text", которое является эквивалентом $string = $string ~ "text".

Оператор => ( => - толстая стрелка ) создает Пару ( pair ) объектов. Пара содержит один ключ и одно значение; ключ располагается слева от оператора =>, а значение - справа. Этот оператор имеет одну особенность: парсер интерпретирует любой идентификатор в левой части выражения как строку. С учетом этой особенности строку из примера можно записать следующим образом:

 
        my @scores = Ana => 8, Dave => 6, Charlie => 4, Beth => 4;
 

И наконец, оператор , создает Парселы ( Parcel ) - последовательности объектов. В данном случае объектами являются пары.

Все три рассмотренные оператора являются инфиксными, то есть располагаются между двумя термами (terms). Термом может быть литерал, например 8 или "Dave", или комбинация других термов и операторов.

В предыдущей главе были использованы также другие типы операторов. Они сдержали инструкцию %games{$p1}++;. Постциркумфиксный (postcircumfix) оператор {...} указан после ( post) терма, и содержит два символа ( открывающую и закрывающую фигурные скобки), которые окружают (circumfix) другой терм. После postcircumfix оператора следует обычный постфиксный оператор ++, который инкрементирует (увеличивает на единицу) переменную слева. Не допускается использование пробела между термом и его постфиксными (postfix) или постциркумфиксным (postcircumfix ) операторами.

Еще один тип операторов - префиксный (prefix). Они указываются перед термом. Примером такого оператора служит -, который инвертирует указанное числовое значение: my $x = -4.

Оператор - еще означает вычитание, поэтому say 5 - 4 напечатает 1. Чтобы отличить префиксный оператор - от инфиксного -, парсер Perl 6 отслеживает контекст: ожидается ли в данный момент инфиксный оператор или терм. У терма может отсутствовать или указано сколько угодно префиксных операторов, то есть возможна следующее выражение : say 4 + -5. В нем, после + ( инфиксного оператора ), компилятор ожидает терм, и поэтому следующий за ним - интерпретируется как префиксный оператор для терма 5.

Следующая строка содержит новые особенности :

        my $label-area-width = 1 + [max] @scores.key.chars;

Начинатся указанная строка с безобидного определения переменной my $label-area-width и оператора присвоения. Затем следует простое операция сложения 1 + .... Правая часть оператора + более сложная. Инфиксный оператор max возвращает большее из двух значений, то есть 2 max 3 вернет 3. Квадратные скобки вокруг оператора дают инструкцию Perl 6 применить указанный в них оператор к списку поэлементно. Поэтому конструкция [max] 1, 5, 3, 7 эквивалентна 1 max 5 max 3 max 7, а результатом будет число 7.

Также можно использовать [+] для получения суммы элементов списка, [*] - произведения и [<=] для проверки отсортирован ли список по убыванию.

Следующим идет выражение @scores.key.chars. Так же, как @variable.method вызывает метод у @variable, @array.method производит вызовы метода для каждого элемента в массиве @array и возвращает список результатов.

представляет собой гипер опрератор. Это также Unicode символ. В случае невозможности ввода данного символа, его можно заменить на два знака больше (>>) . За неимением Ubuntu под рукой следующее решение привожу в оригинале: Ubuntu 10.4: In System/Preferences/Keyboard/Layouts/Options/Compose Key position select one of the keys to be the "Compose" key. Then press Compose-key and the "greater than" key twice.

Результатом @scores.key является список ключей пар в @scores, а @scores.key.chars возвращает список длин ключей в @scores.

Выражение [max]@scores.key.chars выдаст наибольшее из значений. Это так же идентично следующему коду:

        @scores[0].key.chars
           max @scores[1].key.chars
           max @scores[2].key.chars
           max ...

Предваряющие выражение (circumfix) квадратные скобки являются редукционным мета опрератором, который преобразует содержащийся в нем инфиксный оператор в оператор, который ожидает список (listop), а также последовательно осуществляет операции между элементами каждого из списков.

Для отображения имен игроков и столбцов диаграммы, программе необходима информация о количестве позиций на экране, отводимом для имен игроков. Для этого вычисляется максимальная длина имени и прибавляется 1 для отделения имени от начала столбца диаграммы. Полученный результат будет длиной подписи к столбцу диаграммы (с одним уточнением: столбцы - горизотальные ).

Следующий текст определяет наибольшее количество очков:

        my $max-score = [max] @scores.value;

Область диаграммы имеет ширину $screen-width - $label-area-width, равную разнице ширины экрана и длины подписи для данного столбца. Таким образом для каждой строки рейтинга потребуется вывести на экран :

        my $unit = ($screen-width - $label-area-width) / $max-score;

... количество символов X. В процессе вычислений используются инфиксные операторы - и /.

Теперь вся необходимая информация известна и можно построить диаграмму:

        for @scores {
           my $format = '%- ' ~ $label-area-width ~ "s%s\n";
           printf $format, .key, 'X' x ($unit * .value);
        }

Данный код циклически обходит весь список @scores, связывая каждый из элементов со специальной переменной $_. Для каждого элемента используется встроенная функция printf , которая печатает на экране имя игрока и строку диаграммы. Данная функция похожа на printf в языках C и Perl 5. Она получает строку форматирования, которая описывает каким образом печатать следующие за ней параметры. Если $label-area-width равна 8, то строка форматирования будет "%-8s%s\n". Это значит, что строка %s занимает 8 позиций ('8') и выравнена по левому краю, за ней следует еще строка и символ новой строки '\n'. В нашем случае первой строкой является имя игрока. второй - строка диаграммы.

Инфиксный оператор x, или оператор повторения, формирует строку столбца. Он возвращает строку, состоящую из левого операнда, повторенного число раз, заданное правым операндом. То есть 'ab' x 3 вернет строку 'ababab'. .value возвращает значение текущей пары, ($unit * .value) умножает его на $unit, и 'X' x ($unit * .value) возвращает строку с требуемым количеством символов.