В информатике трехфакторное сравнение берет два значения A и B, принадлежащие типу с общим порядком , и определяет, выполняется ли условие A < B, A = B или A > B за одну операцию в соответствии с математическим законом трихотомии .
Его можно реализовать в виде функции (например, strcmp
в C ), метода (например, compareTo
в Java ) или оператора (например, оператора космического корабля <=>
в Perl , PHP и C++ ).
Многие процессоры имеют наборы инструкций , которые поддерживают такую операцию на примитивных типах. Некоторые машины имеют знаковые целые числа, основанные на представлении знака и величины или дополнении до единиц (см. представления знаковых чисел ), оба из которых допускают дифференцированный положительный и отрицательный ноль . Это не нарушает трихотомию, пока принят последовательный общий порядок: либо −0 = +0, либо −0 < +0 является допустимым. Однако распространенные типы с плавающей точкой имеют исключение из трихотомии: существует специальное значение «NaN» ( Not a Number ), такое что x < NaN, x > NaN и x = NaN являются ложными для всех значений с плавающей точкой x (включая сам NaN).
В языке C функции strcmp
и memcmp
выполняют трехстороннее сравнение строк и буферов памяти соответственно. Они возвращают отрицательное число, когда первый аргумент лексикографически меньше второго, ноль, когда аргументы равны, и положительное число в противном случае. Это соглашение о возврате «знака разности» распространяется на произвольные функции сравнения стандартной функцией сортировки qsort
, которая принимает функцию сравнения в качестве аргумента и требует от нее его соблюдения.
В Perl (только для числовых сравнений, cmp
оператор используется для строковых лексических сравнений), PHP (начиная с версии 7), Ruby и Apache Groovy «оператор космического корабля» <=>
возвращает значения −1, 0 или 1 в зависимости от того, A < B, A = B или A > B соответственно. Функции Python 2.x cmp
(удалены в 3.x), OCaml compare
и Kotlin compareTo
вычисляют то же самое. В стандартной библиотеке Haskell функция трехстороннего сравнения определена для всех типов в классе ; она возвращает тип , значения которого (меньше), (равно) и (больше): [1]compare
Ord
Ordering
LT
EQ
GT
Порядок данных = LT | EQ | GT
Во многих объектно-ориентированных языках программирования есть функция трехстороннего сравнения , которая выполняет трехстороннее сравнение между объектом и другим заданным объектом. Например, в Java любой класс, реализующий Comparable
интерфейс, имеет метод compareTo , который либо возвращает отрицательное целое число, ноль или положительное целое число, либо выдает NullPointerException
(если один или оба объекта являются null
). Аналогично, в .NET Framework любой класс, реализующий IComparable
интерфейс, имеет такой метод CompareTo . В C++ любой класс, который может быть сравнен трехсторонне, может быть параметром для экземпляров std::compare_three_way, std::strong_order, std::weak_order или std::partial_order.
Начиная с версии Java 1.5, то же самое можно вычислить с помощью Math.signum
статического метода, если разность может быть известна без вычислительных проблем, таких как арифметическое переполнение, упомянутое ниже. Многие компьютерные языки позволяют определять функции, поэтому compare(A,B) может быть разработано соответствующим образом, но вопрос в том, может ли его внутреннее определение использовать какой-либо трехсторонний синтаксис или же придется прибегнуть к повторным тестам.
При реализации трехстороннего сравнения, когда оператор или метод трехстороннего сравнения еще не доступен, обычно объединяют два сравнения, например, A = B и A < B или A < B и A > B. В принципе, компилятор может сделать вывод, что эти два выражения можно заменить только одним сравнением с последующими множественными проверками результата, но упоминания об этой оптимизации в текстах по этой теме не встречаются.
В некоторых случаях трехстороннее сравнение можно смоделировать, вычитая A и B и проверяя знак результата, используя специальные инструкции для проверки знака числа. Однако для этого требуется, чтобы тип A и B имел четко определенную разницу. Целые числа с фиксированной шириной могут переполняться при вычитании, числа с плавающей точкой имеют значение NaN с неопределенным знаком, а строки символов не имеют функции разности, соответствующей их общему порядку. На уровне машины переполнение обычно отслеживается и может использоваться для определения порядка после вычитания, но эта информация обычно недоступна для языков более высокого уровня.
В одном из случаев трехстороннего условного оператора , предоставляемого языком программирования, ныне устаревший трехсторонний арифметический оператор IF языка Fortran учитывает знак арифметического выражения и предлагает три метки для перехода в соответствии со знаком результата:
ЕСЛИ ( выражение ) отрицательное , ноль , положительное
Распространенная библиотечная функция strcmp в C и родственных языках представляет собой трехстороннее лексикографическое сравнение строк; однако в этих языках отсутствует общее трехстороннее сравнение других типов данных.
Оператор трехстороннего сравнения или «оператор космического корабля» для чисел обозначается <=>
в Perl , Ruby , Apache Groovy , PHP , Eclipse Ceylon и C++ как и называется оператором космического корабля . [2]
В C++ в редакции C++20 добавлен оператор космического корабля <=>
, который возвращает значение, кодирующее, являются ли два значения равными, меньшими, большими или неупорядоченными, и может возвращать различные типы в зависимости от строгости сравнения. [3]
Название произошло от того, что Рэндалу Л. Шварцу оно напомнило космический корабль из игры Star Trek на HP BASIC . [4] Другой программист предположил, что корабль был так назван из-за его сходства с истребителем TIE Дарта Вейдера из саги «Звездные войны» . [5]
Пример на PHP:
эхо 1 <=> 1 ; // 0 эхо 1 <=> 2 ; // -1 эхо 2 <=> 1 ; // 1
Пример на C++:
1 <=> 1 ; // вычисляется как std::strong_ordering::equal 1 <=> 2 ; // вычисляется как std::strong_ordering::less 2 <=> 1 ; // вычисляется как std::strong_ordering::greater
Трехсторонние сравнения обладают тем свойством, что их легко составлять и строить лексикографические сравнения непримитивных типов данных, в отличие от двухсторонних сравнений.
Вот пример композиции на Perl.
подкоманда ($$) { my ( $a , $b ) = @_ ; return $ a -> { unit } cmp $b -> { unit } || $a -> { rank } <=> $b -> { rank } || $a -> { name } cmp $b -> { name }; }
Обратите внимание, что cmp
в Perl — для строк, так как <=>
— для чисел. Двусторонние эквиваленты, как правило, менее компактны, но не обязательно менее разборчивы. Вышеизложенное использует преимущество короткой оценки оператора ||
и тот факт, что 0 считается ложным в Perl. В результате, если первое сравнение равно (и, следовательно, оценивается как 0), оно «провалится» во второе сравнение и так далее, пока не найдет ненулевое или пока не достигнет конца.
В некоторых языках, включая Python , Ruby , Haskell и т. д., сравнение списков выполняется лексикографически, что означает, что можно построить цепочку сравнений, как в приведенном выше примере, помещая значения в списки в желаемом порядке; например, в Ruby:
[ а . подразделение , а . звание , а . имя ] <=> [ б . подразделение , б . звание , б . имя ]
В С++:
std :: tie ( a . unit , a . rank , a . name ) <=> std :: tie ( b . unit , b . rank , b . name )
<=>
синтаксисом в статье под названием «Consistent Comparison». См. «Consistent Comparison». Он был успешно объединен с проектом C++20 в ноябре 2017 года.