Operador relacional - Relational operator

Na ciência da computação , um operador relacional é uma construção ou operador de linguagem de programação que testa ou define algum tipo de relação entre duas entidades . Isso inclui igualdade numérica ( por exemplo , 5 = 5 ) e desigualdades ( por exemplo , 4 ≥ 3 ).

Em linguagens de programação que incluem um tipo de dados booleano distinto em seu sistema de tipos , como Pascal , Ada ou Java , esses operadores geralmente são avaliados como verdadeiro ou falso, dependendo se o relacionamento condicional entre os dois operandos é válido ou não. Em linguagens como C , os operadores relacionais retornam os inteiros 0 ou 1, onde 0 significa falso e qualquer valor diferente de zero significa verdadeiro.

Uma expressão criada usando um operador relacional forma o que é denominado expressão relacional ou condição . Os operadores relacionais podem ser vistos como casos especiais de predicados lógicos .

Igualdade

Uso

A igualdade é usada em muitas construções de linguagem de programação e tipos de dados. É usado para testar se um elemento já existe em um conjunto ou para acessar um valor por meio de uma chave. Ele é usado em instruções switch para despachar o fluxo de controle para a ramificação correta e durante o processo de unificação na programação lógica.

Um possível significado de igualdade é que "se a é igual a b , então a ou b podem ser usados ​​indistintamente em qualquer contexto, sem notar qualquer diferença." Mas essa afirmação não é necessariamente válida, especialmente quando se leva em consideração a mutabilidade junto com a igualdade de conteúdo.

Igualdade de localização vs. igualdade de conteúdo

Às vezes, particularmente na programação orientada a objetos , a comparação levanta questões de tipos de dados e herança , igualdade e identidade . Muitas vezes é necessário distinguir entre:

  • dois objetos diferentes do mesmo tipo, por exemplo, duas mãos
  • dois objetos sendo iguais, mas distintos, por exemplo, duas notas de $ 10
  • dois objetos sendo iguais, mas com representação diferente, por exemplo, uma nota de $ 1 e uma moeda de $ 1
  • duas referências diferentes para o mesmo objeto, por exemplo, dois apelidos para a mesma pessoa

Em muitas linguagens de programação modernas, objetos e estruturas de dados são acessados ​​por meio de referências . Nessas linguagens, torna-se necessário testar dois tipos diferentes de igualdade:

  • Igualdade de localização (identidade): se duas referências (A e B) referenciarem o mesmo objeto. As interações com o objeto por meio de A são indistinguíveis das mesmas interações por meio de B e, em particular, as alterações no objeto por meio de A são refletidas por meio de B.
  • Igualdade de conteúdo: se os objetos referenciados por duas referências (A e B) são equivalentes em algum sentido:
  • Igualdade estrutural (ou seja, seus conteúdos são os mesmos). que pode ser superficial (testando apenas as subpartes imediatas) ou profunda (testando a igualdade das subpartes recursivamente). Uma maneira simples de conseguir isso é por meio da igualdade representacional: verificar se os valores têm a mesma representação.
  • Alguma outra igualdade feita sob medida, preservando o comportamento externo. Por exemplo, 1/2 e 2/4 são considerados iguais quando vistos como um número racional. Um possível requisito seria que "A = B se e somente se todas as operações nos objetos A e B tivessem o mesmo resultado", além de reflexividade , simetria e transitividade .

O primeiro tipo de igualdade geralmente implica o segundo (exceto para coisas como não um número ( NaN ) que são desiguais entre si), mas o inverso não é necessariamente verdadeiro. Por exemplo, dois objetos string podem ser objetos distintos (desiguais no primeiro sentido), mas contêm a mesma sequência de caracteres (iguais no segundo sentido). Veja identidade para mais informações sobre este assunto.

Os números reais, incluindo muitas frações simples , não podem ser representados exatamente na aritmética de ponto flutuante e pode ser necessário testar a igualdade dentro de uma dada tolerância. Tal tolerância, entretanto, pode facilmente quebrar propriedades desejadas, como transitividade, enquanto a reflexividade também quebra: o padrão de ponto flutuante IEEE requer que NaN ≠ NaN seja mantido .

Outros elementos de programação, como funções computáveis, podem não ter senso de igualdade ou uma igualdade que é incomputável. Por essas razões, algumas linguagens definem uma noção explícita de "comparável", na forma de uma classe base, uma interface, um traço ou um protocolo, que é usado explicitamente, por declaração no código-fonte, ou implicitamente, por meio da estrutura do tipo envolvido.

Comparando valores de diferentes tipos

Em JavaScript , PHP , VBScript e algumas outras linguagens digitadas dinamicamente , o operador de igualdade padrão é avaliado como verdadeiro se dois valores forem iguais, mesmo se eles tiverem tipos diferentes, fazendo com que o número 4 compare seja igual à string de texto "4", por exemplo . Um operador de igualdade digitado também está frequentemente disponível em tais linguagens, retornando true apenas para valores com tipos idênticos ou equivalentes (em PHP, 4 === "4"é falso, embora 4 == "4"seja verdadeiro). Para linguagens onde o número 0 pode ser interpretado como falso , este operador pode simplificar coisas como verificar o zero (como x == 0seria verdadeiro se x fosse 0 ou "0" usando o operador de igualdade agnóstico de tipo).

Encomenda

A comparação de maior e menor que de dados não numéricos é realizada de acordo com uma convenção de classificação (como, para sequências de texto, ordem lexicográfica ) que pode ser incorporada à linguagem de programação e / ou configurável por um programador.

Quando se deseja associar um valor numérico ao resultado de uma comparação entre dois itens de dados, digamos a e b , a convenção usual é atribuir −1 se a <b, 0 se a = be 1 se a> b. Por exemplo, a função C strcmpexecuta uma comparação de três vias e retorna -1, 0 ou 1 de acordo com esta convenção, e qsort espera que a função de comparação retorne valores de acordo com esta convenção. Em algoritmos de classificação , a eficiência do código de comparação é crítica, pois é um dos principais fatores que contribuem para o desempenho da classificação.

A comparação de tipos de dados definidos pelo programador ( tipos de dados para os quais a linguagem de programação não tem compreensão embutida) pode ser realizada por funções personalizadas ou de biblioteca (como strcmpmencionado acima) ou, em algumas linguagens, sobrecarregando uma comparação operador - isto é, atribuindo um significado definido pelo programador que depende dos tipos de dados que estão sendo comparados. Outra alternativa é usar alguma convenção, como a comparação entre membros.

Equivalência lógica

Embora talvez não óbvio a princípio, como os operadores lógicos booleanos XOR, AND, OR e NOT, os operadores relacionais podem ser projetados para ter equivalência lógica , de modo que todos possam ser definidos em termos um do outro. As quatro declarações condicionais seguintes têm todos a mesma lógica de equivalência E (seja tudo verdade ou todos false) para qualquer dado x e y valores:

Isso depende do domínio ser bem ordenado .

Operadores relacionais padrão

Os operadores relacionais numéricos mais comuns usados ​​em linguagens de programação são mostrados abaixo.

Operadores relacionais comuns
Convenção igual a não é igual a Maior que Menor que maior
ou igual a
menor
ou igual a
Na impressão = > <
FORTRAN .EQ. .NE. .GT. .LT. .GE. .LE.
ALGOL 68 = > <
/= >= <=
eq ne gt lt ge le
APL = > <
BASIC , ML , Pascal = <> > < >= <=
CAXUMBA = '= > < '< '>
Lua == ~= > < >= <=
Tipo C == != > < >= <=
Erlang == /= > < >= =<
=:= =/=
Conchas tipo Bourne -eq -ne -gt -lt -ge -le
Arquivo de lote 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)
Fortran 90 , Haskell == /= > < >= <=
Mathematica == != > < >= <=
Equal[x,y] Unequal[x,y] Greater[x,y] Less[x,y] GreaterEqual[x,y] LessEqual[x,y]

Outras convenções são menos comuns: Common Lisp e Macsyma / Maxima usam operadores do tipo Basic exceto para desigualdade, que está /=em Common Lisp e #em Macsyma / Maxima. Mais velhos Lisps utilizado equal, greaterpe lessp; e negou-os usando notpara os operadores restantes.

Sintaxe

Os operadores relacionais também são usados ​​na literatura técnica em vez de palavras. Operadores relacionais são geralmente escritos em notação infixa , se suportados pela linguagem de programação, o que significa que eles aparecem entre seus operandos (as duas expressões sendo relacionadas). Por exemplo, uma expressão em Python imprimirá a mensagem se x for menor que y :

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

Outras linguagens de programação, como Lisp , usam notação de prefixo , da seguinte maneira:

(>= X Y)

Cadeia de operador

Em matemática, é prática comum encadear operadores relacionais, como em 3 <x <y <20 (significando 3 <x e x <y e y <20). A sintaxe é clara, pois esses operadores relacionais em matemática são transitivos.

No entanto, muitas linguagens de programação recentes veriam uma expressão como 3 <x <y consistindo em dois operadores associativos à esquerda (ou à direita), interpretando-a como algo semelhante (3 < x) < y. Se dissermos que x = 4, obtemos (3 < 4) < y, e a avaliação dará o true < yque geralmente não faz sentido. No entanto, ele compila em C / C ++ e algumas outras linguagens, produzindo resultados surpreendentes (como verdadeiro seria representado pelo número 1 aqui).

É possível dar à expressão x < y < zseu significado matemático familiar, e algumas linguagens de programação, como Python e Raku, fazem isso. Outros, como C # e Java, não, em parte porque seria diferente da maneira como a maioria dos outros operadores de infixo funcionam em linguagens semelhantes a C. A linguagem de programação D não faz isso, pois mantém alguma compatibilidade com C, e "Permitir expressões C, mas com semânticas sutilmente diferentes (embora indiscutivelmente na direção certa) adicionaria mais confusão do que conveniência".

Algumas linguagens, como Common Lisp , usam vários predicados de argumento para isso. Em Lisp (<= 1 x 10)é verdadeiro quando x está entre 1 e 10.

Confusão com operadores de atribuição

O FORTRAN antigo (1956–57) era limitado por conjuntos de caracteres altamente restritos, onde =era o único operador relacional disponível. Não houve <ou >(e certamente não houve ou ). Isto forçou os designers para definir símbolos, como .GT., .LT., .GE., .EQ.etc. e, posteriormente, tornou tentador usar os restantes =caracteres para copiar, apesar da incoerência óbvia com o uso de matemática ( X=X+1deve ser impossível).

Linguagem Algébrica Internacional (IAL, ALGOL 58 ) e ALGOL (1958 e 1960) assim introduzidos :=para atribuição, deixando o padrão =disponível para igualdade, uma convenção seguida por CPL , ALGOL W , ALGOL 68 , Basic Combined Programming Language ( BCPL ), Simula , Linguagem SET ( SETL ), Pascal , Smalltalk , Modula-2 , Ada , ML padrão , OCaml , Eiffel , Object Pascal ( Delphi ), Oberon , Dylan , VHSIC Hardware Description Language ( VHDL ) e várias outras linguagens.

B e C

Este uniforme padrão de facto entre a maioria das linguagens de programação foi mudado eventualmente, indiretamente, por um minimalista compilado linguagem chamada B . Sua única aplicação pretendida era como um veículo para uma primeira versão do (então muito primitivo) Unix , mas também evoluiu para uma linguagem C muito influente .

B começou como uma variante sintaticamente alterada da linguagem de programação de sistemas BCPL , uma versão simplificada (e sem tipo) de CPL . No que foi descrito como um processo de "desmontagem", os operadores ande orde BCPL foram substituídos por &e |(que mais tarde se tornariam &&e ||, respectivamente.). No mesmo processo, o estilo ALGOL :=de BCPL foi substituído por =em B. O motivo de tudo isso é desconhecido. Como as atualizações de variáveis ​​não tinham sintaxe especial em B (como letou semelhante) e eram permitidas em expressões, esse significado não padrão do sinal de igual significava que a semântica tradicional do sinal de igual agora tinha que ser associada a outro símbolo. Ken Thompson usou a ==combinação ad hoc para isso.

Como um pequeno sistema de tipos foi introduzido posteriormente, B se tornou C. A popularidade dessa linguagem, juntamente com sua associação com Unix, levou a Java, C # e muitas outras linguagens seguindo o exemplo, sintaticamente, apesar desse conflito desnecessário com o significado matemático de o sinal de igual.

línguas

As atribuições em C têm um valor e, como qualquer valor escalar diferente de zero é interpretado como verdadeiro em expressões condicionais , o código if (x = y)é válido , mas tem um significado muito diferente de if (x == y). O fragmento de código anterior significa "atribua y a x , e se o novo valor de x não for zero, execute a seguinte instrução". O último fragmento significa " se e somente se x for igual ay , execute a seguinte instrução".

  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);
  }

Embora Java e C # tenham os mesmos operadores que C, esse erro geralmente causa um erro de compilação nessas linguagens, porque a condição if deve ser do tipo booleane não há maneira implícita de converter de outros tipos ( por exemplo , números) em booleans. Portanto, a menos que a variável atribuída tenha um tipo boolean(ou tipo de invólucro Boolean), haverá um erro de compilação.

Em linguagens como o ALGOL, como Pascal, Delphi e Ada (no sentido de que permitem definições de funções aninhadas ), e em Python , e muitas linguagens funcionais, entre outras, os operadores de atribuição não podem aparecer em uma expressão (incluindo ifcláusulas), portanto impedindo esta classe de erro. Alguns compiladores, como GNU Compiler Collection (GCC), fornecem um aviso ao compilar o código que contém um operador de atribuição dentro de uma instrução if, embora haja alguns usos legítimos de uma atribuição dentro de uma condição if. Nesses casos, a atribuição deve ser agrupada explicitamente em um par extra de parênteses, para evitar o aviso.

Da mesma forma, algumas linguagens, como BASIC, usam apenas o =símbolo para atribuição e igualdade, pois são sintaticamente separados (como Pascal, Ada, Python, etc., os operadores de atribuição não podem aparecer em expressões).

Alguns programadores adquirem o hábito de escrever comparações com uma constante no reverso da ordem usual:

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

Se =for usado acidentalmente, o código resultante será inválido porque 2 não é uma variável. O compilador irá gerar uma mensagem de erro, na qual o operador adequado pode ser substituído. Este estilo de codificação é denominado comparação à esquerda ou condições Yoda .

Esta tabela lista os diferentes mecanismos para testar esses dois tipos de igualdade em vários idiomas:

Língua Igualdade física Igualdade estrutural Notas
ALGOL 68 a :=: b ou a is b a = b quando ae bsão ponteiros
C , C ++ a == b *a == *b quando ae bsão ponteiros
C # object.ReferenceEquals(a, b) a.Equals(b) O ==padrão do operador é ReferenceEquals, mas pode ser sobrecarregado para executar em seu Equalslugar.
Lisp Comum (eq a b) (equal a b)
Erlang a =:= b a == b quando aeb são números
Vai a == b reflect.DeepEqual(*a, *b) quando a e b são ponteiros
Java a == b a.equals(b)
JavaScript a === b a == b quando aeb são dois objetos string contendo caracteres equivalentes, o operador === ainda retornará verdadeiro.
OCaml , Smalltalk a == b a = b
Pascal a^ = b^ a = b
Perl $a == $b $$a == $$b quando $ae $bsão referências a escalares
PHP $a === $b $a == $b quando $ae $bsão objetos
Pitão a is b a == b
Rubi a.equal?(b) a == b
Esquema (eq? a b) (equal? a b)
Rápido a === b a == b quando a e b têm tipo de classe
Visual Basic .NET a Is b ou object.ReferenceEquals(a, b) a = b ou a.Equals(b) Igual a C #
Objective-C ( cacau , GNUstep ) a == b [a isEqual:b] quando ae bsão ponteiros para objetos que são instâncias deNSObject

Ruby costuma a === bsignificar "b é um membro do conjunto a", embora os detalhes do que significa ser um membro variem consideravelmente dependendo dos tipos de dados envolvidos. ===é aqui conhecido como o operador de "igualdade de caso" ou "subsunção de caso".

Veja também

Notas e referências