En programación informática , los operadores son construcciones definidas dentro de lenguajes de programación que se comportan generalmente como funciones , pero que difieren sintáctica o semánticamente .
Ejemplos simples comunes incluyen aritmética (p. ej., suma con +
), comparación (p. ej., " mayor que " con >
) y operaciones lógicasAND
(p. ej. , también escrito &&
en algunos lenguajes). Ejemplos más complejos incluyen asignación (generalmente =
o :=
), acceso a campo en un registro u objeto (generalmente .
) y el operador de resolución de alcance (a menudo ::
o .
). Los lenguajes generalmente definen un conjunto de operadores integrados y, en algunos casos, permiten a los usuarios agregar nuevos significados a operadores existentes o incluso definir operadores completamente nuevos.
Sintácticamente, los operadores suelen contrastar con las funciones . En la mayoría de los lenguajes, las funciones pueden verse como una forma especial de operador de prefijo con un nivel de precedencia y asociatividad fijos, a menudo con paréntesis obligatorios, por ejemplo Func(a)
(o (Func a)
en Lisp ). La mayoría de los lenguajes admiten funciones definidas por el programador, pero no pueden realmente afirmar que admiten operadores definidos por el programador, a menos que tengan más que una notación de prefijo y más de un único nivel de precedencia. Semánticamente, los operadores pueden verse como una forma especial de función con una notación de llamada diferente y un número limitado de parámetros (normalmente 1 o 2).
La posición del operador con respecto a sus operandos puede ser prefijo , infijo o posfijo , y la sintaxis de una expresión que involucra un operador depende de su aridad (número de operandos ), precedencia y (si corresponde), asociatividad . La mayoría de los lenguajes de programación admiten operadores binarios y algunos operadores unarios , y algunos admiten más operandos, como el operador ?: en C, que es ternario. Hay operadores unarios de prefijo, como el unario menos -x
, y operadores unarios de posfijo, como el post-incremento x++
; y las operaciones binarias son infijas, como x + y
o x = y
. Las operaciones infijas de mayor aridad requieren símbolos adicionales, como el operador ternario ?: en C, escrito como a ? b : c
– de hecho, dado que este es el único ejemplo común, a menudo se lo conoce como operador ternario. Sin embargo, las operaciones de prefijo y posfijo pueden admitir cualquier aridad deseada, como 1 2 3 4 +
.
Ocasionalmente, [1] [2] partes de un lenguaje pueden describirse como operadores " matchfix " o " circunfix " [3] [4] , ya sea para simplificar la descripción o implementación del lenguaje. Un operador circunfijo consta de dos o más partes que encierran sus operandos. Los operadores circunfijos tienen la mayor precedencia, y se evalúa su contenido y el valor resultante se usa en la expresión circundante. El operador circunfijo más conocido son los paréntesis mencionados anteriormente, que se usan para indicar qué partes de una expresión se deben evaluar antes que otras. Otro ejemplo de la física es la notación de producto interno de la notación corchete de Dirac . Los operadores circunfijos son especialmente útiles para denotar operaciones que involucran muchos o variados números de operandos.
La especificación de un lenguaje especificará la sintaxis de los operadores que admite, mientras que los lenguajes como Prolog que admiten operadores definidos por el programador requieren que la sintaxis sea definida por el programador.
La semántica de los operadores depende en particular del valor, la estrategia de evaluación y el modo de paso de argumentos (como el cortocircuito booleano). En pocas palabras, una expresión que involucra un operador se evalúa de alguna manera y el valor resultante puede ser simplemente un valor (un valor r) o puede ser un objeto que permite la asignación (un valor l).
En casos simples, esto es idéntico a las llamadas de función habituales; por ejemplo, la adición x + y
generalmente es equivalente a una llamada de función add(x, y)
y la comparación menor que x < y
a lt(x, y)
, lo que significa que los argumentos se evalúan de su forma habitual, luego se evalúa alguna función y el resultado se devuelve como un valor. Sin embargo, la semántica puede ser significativamente diferente. Por ejemplo, en la asignación, a = b
el objetivo a
no se evalúa, sino que su ubicación (dirección) se usa para almacenar el valor de b
– correspondiente a la semántica de llamada por referencia . Además, una asignación puede ser una declaración (sin valor) o puede ser una expresión (valor), con el valor en sí mismo como un valor r (solo un valor) o un valor l (que se puede asignar a). Como otro ejemplo, el operador de resolución de ámbito :: y el operador de acceso a elementos . (como en Foo::Bar
o a.b
) no operan sobre valores, sino sobre nombres , esencialmente semántica de llamada por nombre , y su valor es un nombre.
El uso de valores l como operandos de operador es particularmente notable en los operadores unarios de incremento y decremento . En C, por ejemplo, la siguiente declaración es legal y está bien definida, y depende del hecho de que la indexación de matrices devuelve un valor l:
x = ++ a [ i ];
Un uso importante es cuando un operador binario asociativo por la izquierda modifica su argumento izquierdo (o produce un efecto secundario ) y luego evalúa ese argumento como un valor l. [a] Esto permite una secuencia de operadores que afectan al argumento original, lo que permite una interfaz fluida , similar a la cascada de métodos . Un ejemplo común es el <<
operador en la biblioteca C++ iostream
, que permite una salida fluida, como sigue:
cout << "Hola" << " " << "¡mundo!" << endl ;
Un lenguaje puede contener un número fijo de operadores integrados (p. ej. +, -, *, <, <=, !, = , etc. en C y C++ , PHP ), o puede permitir la creación de operadores definidos por el programador (p. ej. Prolog , [5] Seed7 , [6] F# , OCaml , Haskell ). Algunos lenguajes de programación restringen los símbolos de los operadores a caracteres especiales como + o := mientras que otros permiten también nombres como (p. ej. Pascal ).div
La mayoría de los lenguajes tienen un conjunto de operadores incorporado, pero no permiten operadores definidos por el usuario, ya que esto complica significativamente el análisis. [b] Muchos lenguajes solo permiten el uso de operadores para tipos incorporados, pero otros permiten el uso de operadores existentes para tipos definidos por el usuario; esto se conoce como sobrecarga de operadores . Sin embargo, algunos lenguajes permiten definir nuevos operadores, ya sea en tiempo de compilación o en tiempo de ejecución. Esto puede implicar metaprogramación (especificar los operadores en un lenguaje separado) o dentro del lenguaje mismo. La definición de nuevos operadores, particularmente la definición en tiempo de ejecución, a menudo hace imposible el análisis estático correcto de los programas, ya que la sintaxis del lenguaje puede ser Turing-completa, por lo que incluso la construcción del árbol de sintaxis puede requerir la solución del problema de detención, lo cual es imposible. Esto ocurre con Perl , por ejemplo, y algunos dialectos de Lisp .
Ejemplos comunes que difieren de las funciones sintácticamente son los operadores relacionales , por ejemplo, ">" para " mayor que ", con nombres a menudo fuera del conjunto de identificadores del lenguaje para funciones, y llamados con una sintaxis diferente de la sintaxis del lenguaje para llamar a funciones. Como función, "mayor que" generalmente se nombraría con un identificador, como gt
o greater_than
y se llamaría como una función, como gt(x, y)
. En cambio, la operación usa el carácter especial >
(que se tokeniza por separado durante el análisis léxico ) y la notación infija, como x > y
.
Ejemplos comunes que difieren semánticamente (según el modo de paso de argumentos) son las operaciones booleanas, que frecuentemente presentan una evaluación de cortocircuito : por ejemplo, una conjunción de cortocircuito (X AND Y) que solo evalúa argumentos posteriores si los anteriores no son falsos, en un lenguaje con funciones estrictas de llamada por valor. Esto, en cambio, se comporta de manera similar a if/then/else.
Los operadores menos comunes incluyen:
e, f
*p
y operador de dirección:&x
number = spell_out_numbers ? "forty-two" : 42
x ?: y
x ?? y
x <=> y
+=
, -=
, *=
, /=
, %=
, <<=
, >>=
, , &=
, ^=
, |=
De manera similar, algunos procesadores de señales digitales proporcionan códigos de operación especiales para operaciones fusionadas como multiplicar-acumular (MAC/MAD) o multiplicar-suma fusionada (FMA) y algunas bibliotecas de software de alto rendimiento admiten funciones como cis x = cos x + i sen x para aumentar la velocidad de procesamiento o reducir el tamaño del código.Un compilador puede implementar operadores y funciones con llamadas a subrutinas o con código en línea . Algunos operadores integrados que admite un lenguaje tienen una asignación directa a una pequeña cantidad de instrucciones que se encuentran comúnmente en las unidades centrales de procesamiento , aunque otros ( por ejemplo, '+' utilizado para expresar la concatenación de cadenas ) pueden tener implementaciones complicadas.
En algunos lenguajes de programación, un operador puede ser polimórfico ad hoc , es decir, tener definiciones para más de un tipo de datos (como en Java , donde el +
operador se utiliza tanto para la suma de números como para la concatenación de cadenas). Se dice que un operador de este tipo está sobrecargado . En lenguajes que admiten la sobrecarga de operadores por parte del programador (como C++ ) pero que tienen un conjunto limitado de operadores, la sobrecarga de operadores se utiliza a menudo para definir usos personalizados para los operadores.
En el ejemplo , los operadores son: (mayor que) y (menor que).IF ORDER_DATE > "12/31/2011" AND ORDER_DATE < "01/01/2013" THEN CONTINUE ELSE STOP
>
AND
<
Algunos lenguajes también permiten que los operandos de un operador se conviertan implícitamente, o se conviertan , en tipos de datos adecuados para que se realice la operación. Por ejemplo, en Perl, las reglas de conversión conducen a 12 + "3.14"
producir el resultado de 15.14
. El texto "3.14"
se convierte al número 3.14 antes de que se pueda realizar la suma. Además, 12
es un número entero y 3.14
es un número flotante o de punto fijo (un número que tiene un decimal en él), por lo que el entero se convierte a un número de punto flotante o de punto fijo respectivamente.
JavaScript sigue reglas opuestas: al encontrar la misma expresión anterior, convertirá el entero 12
en una cadena "12"
y luego concatenará los dos operandos para formar "123.14"
.
En presencia de coerciones en un lenguaje, el programador debe conocer las reglas específicas respecto a los tipos de operandos y el tipo de resultado de la operación para evitar errores sutiles de programación.
La siguiente tabla muestra las características del operador en varios lenguajes de programación:
@
requiere analizar léxicamente y convertir en tokens este carácter, y la estructura de la frase (árbol sintáctico) depende de la aridad y la precedencia de este operador.