Сравнения и "Умное" сопоставление

Есть несколько способов сравнения объектов в Perl. Можно проверить равенство значений используя инфиксный оператор ===. Для неизменных (immutable) объектов ( значения которых нельзя изменить, литералов. Например литерал 7 всегда будет 7) это обычное сравнение значений. Например 'hello'==='hello' всегда верно потому, что обе строки неизменны и имеют одинаковое значение.

Для изменяемых объектов === сравнивает их идентичность. === возвращает истину, если его аргументы являются псевдонимами одного и того же объекта. Или же двое объектов идентичны, если это один и тот же объект. Даже если оба массива @a и @b содержат одинаковые значения, если их контейнеры разные объекты, они будут иметь различные идентичности и не будут тождественны при сравнении ===:

        my @a = 1, 2, 3;
        my @b = 1, 2, 3;

        say @a  === @a;        # 1
        say @a  === @b;        # 0

        # здесь используется идентичность 
        say 3   === 3;         # 1
        say 'a' === 'a';   # 1

        my $a = 'a';
        say $a === 'a';         # 1

Оператор eqv возвращает Истина если два объекта одного типа и одинаковой структуры. Так для @a и @b указанных в примере, @a eqv @b истинно потому, что @a и @b содержат одни и те же значения. С другой стороны '2' eqv 2 вернет False, так как аргумент слева строка, а справа - число, и таким образом они разных типов.

Сравнения чисел

Вы можете узнать, равны ли числовые значения двух объектов с помощью инфиксного оператора ==. Если один из объектов не числовой, Perl произведет его преобразование в число перед сравнением. Если не будет подходящего способа преобразовать объект в число, Perl будет использовать 0 в качестве значения.

        say 1 == 1.0;          # 1
        say 1 == '1';          # 1
        say 1 == '2';          # 0
        say 3 == '3b'          # 1

Операторы <, <=, >=, и > - являются числовыми операторами сравнения и возвращают логическое значение сравнения. != возвращает True (Истина), если числовые значения объектов различны.

Если сравниваются списки или массивы,то вычисляется количество элементов в списке.

        my @colors = <red blue green>;

        if @colors == 3 {
           say "It's true, @colors contains 3 items";
        }

Сравнение строк

Так же как == преобразует свои аргументы в числа, инфиксный оператор eq сравнивает равенство строк, преобразуя аргументы в строки при необходимости.

        if $greeting eq 'hello' {
           say 'welcome';
        }

Другие операторы сравнивают строки лексикографически.

Таблица 3.2. Операторы и сравнения

ЧисловыеСтроковыеЗначение
==eqравно (equals)
!=neне равно (not equal)
!==!eqне равно (not equal)
<ltменьше чем ( less than )
<=leменьше или равно (less or equal)
>gtбольше чем ( greater than )
>=geбольше или равно ( greater or equal )

Например, 'a' lt 'b' вернет истину, так же как 'a' lt 'aa'.

!= на самом деле более удобная форма для !==, который в свою очередб представляет собой объединеие метаоператора ! и инфиксного оператора ==. Такое же обяснение приминительно к ne и !eq.

Three-way сравнение

Операторы three-way сравнения получают два операнда и возвращают Order::Increase, если операнд слева меньше, Order::Same - если равны, Order::Decrease - если операнд справа меньше (Order::Increase, Order::Same и Order::Decrease являются перечислениями ( enums ); см. ). Для числовых сравнений используется оператор <=>, а для строковых это leg (от англ. lesser, equal, greater). Инфиксный оператор cmp также является оператором сравнения, возвращающий три результата сравнения. Его особенность в том, что он зависит от типа аргументов: числа сравнивает как <=>, строки как leg и ( например) пары сначала сравнивая ключи, а затем значения (если ключи равны).

        say 10   <=> 5;           # +1
        say 10   leg 5;           # because '1' lt '5'
        say 'ab' leg 'a';         # +1, lexicographic comparison

Типичным применением упомянутых three-way операторов сравнения является сортировка. Метод .sort в списках получает блок или функцию, которые сравнивают свои два аргумента и возвращают значения отрицательные если меньше, 0 - если аргументы равны и больше 0, если первый аргумент больше второго. Эти результаты затем используются при сортировке для формирования результата.

        say ~<abstract Concrete>.sort;
        # output: Concrete abstract

        say ~<abstract Concrete>.sort:
               -> $a, $b { uc($a) leg uc($b) };
        # output: abstract Concrete

По умолчанию используется сортировка чувствительная к регистру, т.е. символы в верхнем регистре "больше" символов в нижем. В примере используется сортировка без учета регистра.

"Умное" сопоставление

Разные операторы сравнения приводит свои аргументы к определённым типам перед сравнением их. Это полезно, когда требуется конкретное сравнение, но типы параметров неизвестны. Perl 6 предоставляет особый оператор который позволяет производить сравнение "Делай Как Надо" (Do The Right Thing ) с помощью ~~ - оператора "умного" сравнения.

        if $pints-drunk ~~ 8 {
           say "Go home, you've had enough!";
        }

        if $country ~~ 'Sweden' {
           say "Meatballs with lingonberries and potato moose, please."
        }

        unless $group-size ~~ 2..4 {
           say "You must have between 2 and 4 people to book this tour.";
        }

Оператор "умного" сопоставления всегда решает какого рода сравнение производить в зависимости от типа значения в правой части. В предыдущих примерах эти сравнения были числовым, строковым и сравнением диапазонов соответственно. В данной главе была продемонстрирована работа операторов сравнения: чисел - == и строк eq.Однако нет оператора для сравнения диапазонов. Это является частью возможностей "умного" сопоставления: более сложные типы позволяют реализовывать необычные идеи сочетая сравнения их с другими.

"Умное" сопоставление работает, вызывая метод ACCEPTS у правого операнда и передавая ему операнд слева как аргумент. Выражение $answer ~~ 42 сводится к вызову 42.ACCEPTS($answer). Данная информация пригодится, когда вы прочитаете последующие главы, посвященные классам и методам. Вы тоже напишите вещи, которые смогут производить "умное" сопоставление, реализовав метод ACCEPTS для того, чтобы "работало как надо".