Оператор отношения - Relational operator

В информатике , реляционная операция представляет собой язык программирования конструкта или оператор , что тесты или определяют некоторый вид связи между двумя объектами . К ним относятся числовое равенство ( например , 5 = 5 ) и неравенства ( например , 4 ≥ 3 ).

В языках программирования, которые включают отдельный логический тип данных в свою систему типов , таких как Pascal , Ada или Java , эти операторы обычно принимают значение true или false, в зависимости от того, выполняется ли условная связь между двумя операндами или нет. В таких языках, как C , реляционные операторы возвращают целые числа 0 или 1, где 0 означает ложь, а любое ненулевое значение означает истину.

Выражение создается с помощью реляционной формы оператора , что называется относительное выражение или условие . Операторы отношения можно рассматривать как частные случаи логических предикатов .

Равенство

Применение

Равенство используется во многих конструкциях языков программирования и типах данных. Он используется для проверки того, существует ли уже элемент в наборе , или для доступа к значению с помощью ключа. Он используется в операторах switch для направления потока управления в нужную ветвь и во время процесса унификации в логическом программировании.

Одно из возможных значений равенства состоит в том, что «если a равно b , то либо a, либо b могут использоваться взаимозаменяемо в любом контексте, не замечая никакой разницы». Но это утверждение не обязательно верно, особенно если принять во внимание изменчивость вместе с равенством содержания.

Равенство местоположения и равенство контента

Иногда, особенно в объектно-ориентированном программировании , при сравнении возникают вопросы о типах данных и наследовании , равенстве и идентичности . Часто бывает необходимо различать:

  • два разных объекта одного типа, например, две руки
  • два одинаковых, но разных объекта, например, две банкноты по 10 долларов
  • два объекта, которые равны, но имеют разное представление, например, банкнота в 1 доллар и монета в 1 доллар
  • две разные ссылки на один и тот же объект, например, два псевдонима для одного и того же человека

Во многих современных языках программирования доступ к объектам и структурам данных осуществляется через ссылки . В таких языках возникает необходимость проверки двух разных типов равенства:

  • Равенство расположения (идентичность): если две ссылки (A и B) ссылаются на один и тот же объект. Взаимодействия с объектом через A неотличимы от тех же взаимодействий через B, и, в частности, изменения объекта через A отражаются через B.
  • Равенство содержимого: если объекты, на которые ссылаются две ссылки (A и B), в некотором смысле эквивалентны:
  • Структурное равенство (то есть их содержание одинаково). который может быть либо поверхностным (тестирование только непосредственных частей), либо глубоким (проверка равенства частей рекурсивно). Простым способом добиться этого является репрезентативное равенство: проверка того, что значения имеют одинаковое представление.
  • Какое-то другое индивидуальное равенство, сохраняющее внешнее поведение. Например, 1/2 и 2/4 считаются равными, если рассматривать их как рациональные числа. Возможным требованием может быть то, что «A = B тогда и только тогда, когда все операции с объектами A и B будут иметь одинаковый результат», в дополнение к рефлексивности , симметрии и транзитивности .

Первый тип равенства обычно подразумевает второй (за исключением таких вещей, как не число ( NaN ), которые не равны сами себе), но обратное не обязательно верно. Например, два строковых объекта могут быть разными объектами (неравными в первом смысле), но содержать одну и ту же последовательность символов (равными во втором смысле). См. Личность для получения дополнительной информации об этой проблеме.

Действительные числа, включая множество простых дробей , не могут быть точно представлены в арифметике с плавающей запятой , и может потребоваться проверка на равенство в пределах заданного допуска. Однако такая толерантность может легко нарушить желаемые свойства, такие как транзитивность, тогда как рефлексивность также нарушается: стандарт IEEE с плавающей запятой требует, чтобы выполнялось NaN ≠ NaN .

Другие элементы программирования, такие как вычислимые функции, могут либо не иметь чувства равенства, либо равенства, которое невозможно вычислить. По этим причинам некоторые языки определяют явное понятие «сопоставимого» в форме базового класса, интерфейса, признака или протокола, которое используется либо явно, путем объявления в исходном коде, либо неявно, через структуру вовлеченного типа.

Сравнение значений разных типов

В JavaScript , PHP , VBScript и некоторых других языках с динамической типизацией стандартный оператор равенства принимает значение true, если два значения равны, даже если они имеют разные типы, в результате чего число 4 сравнивается с текстовой строкой «4», например . В таких языках часто доступен типизированный оператор равенства, возвращающий истину только для значений с идентичными или эквивалентными типами (в PHP 4 === "4"это ложь, хотя 4 == "4"и истинно). Для языков, в которых число 0 может быть интерпретировано как ложное , этот оператор может упростить такие вещи, как проверка на ноль (как x == 0было бы верно для x, равного 0 или «0» с использованием оператора равенства, не зависящего от типа).

Заказ

Сравнение больше и меньше нечисловых данных выполняется в соответствии с соглашением о сортировке (например, для текстовых строк, лексикографическим порядком ), которое может быть встроено в язык программирования и / или конфигурируется программистом.

Когда желательно связать числовое значение с результатом сравнения между двумя элементами данных, скажем, a и b , обычным соглашением является присвоение -1, если a <b, 0, если a = b, и 1, если a> b. Например, функция C strcmpвыполняет трехстороннее сравнение и возвращает -1, 0 или 1 в соответствии с этим соглашением, а qsort ожидает, что функция сравнения вернет значения в соответствии с этим соглашением. В алгоритмах сортировки эффективность кода сравнения имеет решающее значение, поскольку это один из основных факторов, влияющих на производительность сортировки.

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

Логическая эквивалентность

Хотя поначалу это может быть неочевидным, подобно логическим логическим операторам XOR, AND, OR и NOT, реляционные операторы могут быть разработаны так, чтобы иметь логическую эквивалентность , так что все они могут быть определены в терминах друг друга. Следующие четыре условных оператора имеют одинаковую логическую эквивалентность E (либо все истинные, либо все ложные) для любых заданных значений x и y :

Это зависит от того, насколько хорошо упорядочен домен .

Стандартные реляционные операторы

Наиболее распространенные числовые реляционные операторы, используемые в языках программирования, показаны ниже.

Общие реляционные операторы
соглашение равно не равно больше чем меньше, чем больше
или равно
меньше
или равно
В печати знак равно > <
FORTRAN .EQ. .NE. .GT. .LT. .GE. .LE.
АЛГОЛ 68 = > <
/= >= <=
eq ne gt lt ge le
APL = > <
БАЗОВЫЙ , ML , Паскаль = <> > < >= <=
Швабры = '= > < '< '>
Lua == ~= > < >= <=
C-подобный == != > < >= <=
Erlang == /= > < >= =<
=:= =/=
Борноподобные снаряды -eq -ne -gt -lt -ge -le
Пакетный файл EQU NEQ GTR LSS GEQ LEQ
MATLAB == ~= > < >= <=
eq(x,y) ne(x,y) gt(x,y) lt(x,y) ge(x,y) le(x,y)
Фортран 90 , Хаскелл == /= > < >= <=
Mathematica == != > < >= <=
Equal[x,y] Unequal[x,y] Greater[x,y] Less[x,y] GreaterEqual[x,y] LessEqual[x,y]

Другие соглашения менее распространены: Common Lisp и Macsyma / Maxima используют операторы, подобные Basic, за исключением неравенства, которое есть /=в Common Lisp и #Macsyma / Maxima. Старые Лиспы используются equal, greaterpи lessp; и отменил их, используя notдля остальных операторов.

Синтаксис

Операторы отношения также используются в технической литературе вместо слов. Операторы отношения обычно записываются в инфиксной нотации , если они поддерживаются языком программирования, что означает, что они появляются между своими операндами (двумя связанными выражениями). Например, выражение в Python напечатает сообщение, если x меньше y :

if x < y:
    print("x is less than y in this example")

Другие языки программирования, такие как Lisp , используют префиксную нотацию , как показано ниже:

(>= X Y)

Цепочка операторов

В математике, обычная практика цепи реляционных операторов, например, в 3 <х <у <20 ( что означает 3 <х и х <у и у <20). Синтаксис ясен, поскольку эти реляционные операторы в математике транзитивны.

Однако многие современные языки программирования увидят выражение типа 3 <x <y как состоящее из двух левых (или правых) ассоциативных операторов, интерпретируя его как нечто вроде (3 < x) < y. Если мы скажем, что x = 4, мы получим (3 < 4) < y, и оценка даст, true < yчто обычно не имеет смысла. Тем не менее, он компилируется на C / C ++ и некоторых других языках, что дает удивительный результат (поскольку истина здесь представлена ​​цифрой 1).

Выражению можно придать x < y < zзнакомый математический смысл, и некоторые языки программирования, такие как Python и Raku, делают это. Другие, такие как C # и Java, этого не делают, отчасти потому, что это будет отличаться от того, как большинство других инфиксных операторов работают в C-подобных языках. Язык программирования D этого не делает, поскольку он поддерживает некоторую совместимость с C, и «разрешение выражений C, но с несколько иной семантикой (хотя, возможно, в правильном направлении) добавило бы больше путаницы, чем удобства».

Некоторые языки, например Common Lisp , используют для этого предикаты с несколькими аргументами. В Лиспе (<= 1 x 10)истинно, когда x находится между 1 и 10.

Путаница с операторами присваивания

Ранний FORTRAN (1956–57) был ограничен сильно ограниченными наборами символов, и это =был единственный доступный оператор отношения. Не было <или >(и уж точно нет или ). Это заставило конструкторов определить символы , такие как .GT., .LT., .GE., и .EQ.т.д. , а затем сделал его заманчиво использовать оставшийся =символ для копирования, несмотря на очевидную непоследовательность с математическим использованием ( X=X+1должно быть невозможно).

Таким образом, для присвоения были введены международный алгебраический язык (IAL, ALGOL 58 ) и ALGOL (1958 и 1960) :=, оставив стандарт =доступным для равенства, соглашение, за которым следуют CPL , ALGOL W , ALGOL 68 , базовый комбинированный язык программирования ( BCPL ), Simula , SET Language ( SETL ), Pascal , Smalltalk , Modula-2 , Ada , Standard ML , OCaml , Eiffel , Object Pascal ( Delphi ), Oberon , Dylan , VHSIC Hardware Description Language ( VHDL ) и несколько других языков.

B и C

Эта униформа де - факто стандарта среди большинства языков программирования была в конечном счете изменилась, косвенно, минималистским скомпилирован языком именем B . Его единственное предполагаемое приложение было средством для первого порта (тогда еще очень примитивного) Unix , но оно также превратилось в очень влиятельный язык C.

B начинался как синтаксически измененный вариант языка системного программирования BCPL , упрощенная (и безтиповая) версия CPL . В процессе, который был описан как «разборка», операторы andи orBCPL были заменены на &и |(которые позже стали &&и ||соответственно). В том же процессе стиль :=BCPL ALGOL был заменен на =B. Причина всего этого неизвестна. Поскольку обновления переменных не имели специального синтаксиса в B (например, letили подобного) и были разрешены в выражениях, это нестандартное значение знака равенства означало, что традиционная семантика знака равенства теперь должна была быть связана с другим символом. Кен Томпсон использовал ==для этого специальную комбинацию.

Поскольку позже была введена небольшая система типов, B затем стал C. Популярность этого языка вместе с его ассоциацией с Unix привела к синтаксическому развитию Java, C # и многих других языков, которые следовали этому примеру, несмотря на этот ненужный конфликт с математическим значением слова знак равенства.

Языки

Присваивания в C имеют значение, и поскольку любое ненулевое скалярное значение интерпретируется как истинное в условных выражениях , код if (x = y)является допустимым, но имеет совсем другое значение от if (x == y). Первое означает фрагмент кода «правопреемником у к х , и если новое значение х не равно нулю, выполнить следующий оператор». Последний фрагмент означает « тогда и только тогда, когда x равно y , выполнить следующую инструкцию».

  int x = 1;
  int y = 2;
  if (x = y) {
      /* This code will always execute if y is anything but 0*/
      printf("x is %d and y is %d\n", x, y);
  }

Хотя в Java и C # используются те же операторы, что и в C, эта ошибка обычно вызывает ошибку компиляции на этих языках, потому что условие if должно иметь тип boolean, и не существует неявного способа преобразования из других типов ( например , чисел) в booleanс. Поэтому, если переменная, присвоенная переменной, не имеет типа boolean(или типа оболочки Boolean), произойдет ошибка компиляции.

В АЛГОЛ-подобных языках, таких как Pascal, Delphi и Ada (в том смысле, что они допускают определения вложенных функций ), а также в Python и многих функциональных языках, среди прочего, операторы присваивания не могут появляться в выражении (включая ifпредложения), таким образом исключая этот класс ошибок. Некоторые компиляторы, такие как GNU Compiler Collection (GCC), выдают предупреждение при компиляции кода, содержащего оператор присваивания внутри оператора if, хотя есть некоторые законные способы использования присваивания внутри условия if. В таких случаях присвоение должно быть явно заключено в дополнительную пару круглых скобок, чтобы избежать предупреждения.

Точно так же некоторые языки, такие как BASIC, используют только =символ как для присваивания, так и для равенства, поскольку они синтаксически разделены (как в Pascal, Ada, Python и т. Д., Операторы присваивания не могут появляться в выражениях).

Некоторые программисты имеют привычку писать сравнения с константами в обратном обычному порядке:

  if (2 == a) {   /* Mistaken use of = versus == would be a compile-time error */
  }

Если =используется случайно, полученный код недействителен, потому что 2 не является переменной. Компилятор выдаст сообщение об ошибке, в котором можно заменить соответствующий оператор. Этот стиль кодирования называется левосторонним сравнением или условиями Йоды .

В этой таблице перечислены различные механизмы проверки этих двух типов равенства на разных языках:

Язык Физическое равенство Структурное равенство Заметки
АЛГОЛ 68 a :=: b или же a is b a = b когда aи bявляются указателями
C , C ++ a == b *a == *b когда aи bявляются указателями
C # object.ReferenceEquals(a, b) a.Equals(b) По ==умолчанию для оператора используется значение ReferenceEquals, но для выполнения вместо этого его можно перегрузитьEquals .
Common Lisp (eq a b) (equal a b)
Erlang a =:= b a == b когда a и b числа
Идти a == b reflect.DeepEqual(*a, *b) когда a и b указатели
Ява a == b a.equals(b)
JavaScript a === b a == b когда a и b являются двумя строковыми объектами, содержащими эквивалентные символы, оператор === все равно вернет true.
OCaml , Smalltalk a == b a = b
Паскаль a^ = b^ a = b
Perl $a == $b $$a == $$b когда $aи $bявляются ссылками на скаляры
PHP $a === $b $a == $b когда $aи $bявляются объектами
Python a is b a == b
Рубин a.equal?(b) a == b
Схема (eq? a b) (equal? a b)
Быстрый a === b a == b когда a и b имеют тип класса
Visual Basic .NET a Is b или же object.ReferenceEquals(a, b) a = b или же a.Equals(b) То же, что C #
Objective-C ( Какао , GNUstep ) a == b [a isEqual:b] когда aи bявляются указателями на объекты, которые являются экземплярамиNSObject

Ruby использует a === bдля обозначения «b является членом множества a», хотя детали того, что значит быть членом, значительно различаются в зависимости от задействованных типов данных. ===здесь известен как оператор «равенство случаев» или «подчинение случаев».

Смотрите также

Примечания и ссылки