En la programación de computadoras , los operadores son construcciones definidas dentro de los lenguajes de programación que se comportan generalmente como funciones , pero que difieren sintáctica o semánticamente .
Los ejemplos simples comunes incluyen aritmética (por ejemplo, suma con +
), comparación (por ejemplo, "mayor que" con >
) y operaciones lógicas (por ejemplo AND
, también escritas &&
en algunos idiomas). Los ejemplos más complicados incluyen la asignación (generalmente =
o :=
), el acceso al 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 los operadores existentes o incluso definir operadores completamente nuevos.
Sintaxis
Sintácticamente, los operadores generalmente contrastan con las funciones . En la mayoría de los lenguajes, las funciones pueden verse como una forma especial de operador de prefijo con nivel de precedencia fijo y asociatividad, 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 en realidad no pueden pretender admitir operadores definidos por el programador, a menos que tengan más de una notación de prefijo y más de un 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 (generalmente 1 o 2).
La posición del operador con respecto a sus operandos puede ser prefijo , infijo o sufijo , y la sintaxis de una expresión que involucra a 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 menos unario -x
, y operadores unarios de sufijo, como post-incremento x++
; y las operaciones binarias son infijas, como x + y
o x = y
. Operaciones infijos de mayor aridad requieren símbolos adicionales, tales como el operador ternario :? En C, escrito como a ? b : c
- de hecho, ya que este es el único ejemplo común, se refiere a menudo como el operador ternario. Sin embargo, las operaciones de prefijo y sufijo pueden admitir cualquier aridad deseada, como 1 2 3 4 +
.
Ocasionalmente [1] [2] partes de un lenguaje pueden describirse como operadores "matchfix" o "circumfix" [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 de circunferencia tienen la prioridad más alta, con su contenido siendo evaluado y el valor resultante usado en la expresión circundante. El operador de circunfijo más familiar son los paréntesis mencionados anteriormente, que se utilizan para indicar qué partes de una expresión deben evaluarse antes que otras. Otro ejemplo de la física es la notación del producto interno de la notación bra-ket de Dirac . Los operadores de circunferencia son especialmente útiles para denotar operaciones que involucran muchos o diferentes números de operandos.
La especificación de un lenguaje especificará la sintaxis de los operadores que admite, mientras que lenguajes como Prolog que admiten operadores definidos por el programador requieren que el programador defina la sintaxis.
Semántica
La semántica de los operadores depende particularmente del valor, la estrategia de evaluación y el modo de paso de argumentos (como el cortocircuito booleano). Simplemente, una expresión que involucra a un operador se evalúa de alguna manera, y el valor resultante puede ser solo 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 suma x + y
es generalmente equivalente a una llamada de función add(x, y)
y una comparación menor x < y
a lt(x, y)
, lo que significa que los argumentos se evalúan de la manera 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 un valor r (solo un valor) o un valor l (que se puede asignar a). Como otro ejemplo, el operador de resolución de alcance :: y el operador de acceso al elemento. (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 el argumento original, lo que permite una interfaz fluida , similar al método en cascada . Un ejemplo común es el <<
operador en la iostream
biblioteca C ++ , que permite una salida fluida, como sigue:
cout << "Hola" << "" << "mundo!" << endl ;
Operadores definidos por el usuario
Un lenguaje puede contener un número fijo de operadores integrados (por ejemplo , +, -, *, <, <=,!, = , Etc. en C y C ++ , PHP ), o puede permitir la creación de operadores definidos por el programador. (por ejemplo , Prolog , [5] Seed7 , [6] F # , OCaml , Haskell ). Algunos lenguajes de programación restringen los símbolos del operador a caracteres especiales como + o : = mientras que otros permiten también nombres comodiv
(por ejemplo,Pascal).
La mayoría de los lenguajes tienen un conjunto integrado de operadores, pero no permiten operadores definidos por el usuario, ya que esto complica significativamente el análisis. [b] Muchos lenguajes solo permiten que se utilicen operadores para tipos integrados, pero otros permiten que los operadores existentes se utilicen para tipos definidos por el usuario; esto se conoce como sobrecarga del operador . Sin embargo, algunos lenguajes permiten definir nuevos operadores, ya sea en tiempo de compilación o en tiempo de ejecución. Esto puede involucrar metaprogramación (especificando 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 sintáctico puede requerir resolver el problema de detención, lo cual es imposible. Esto ocurre para Perl , por ejemplo, y algunos dialectos de Lisp .
Ejemplos de
Ejemplos comunes que difieren sintácticamente son operaciones aritméticas matemáticas , 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 funciones de llamada. Como función, "mayor que" generalmente se nombraría mediante un identificador, como gt
o, greater_than
y se llamaría como 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
.
Los ejemplos comunes que difieren semánticamente (por el modo de paso de argumentos) son operaciones booleanas, que con frecuencia cuentan con evaluación de cortocircuito : por ejemplo, una conjunción de cortocircuito (XY Y) que solo evalúa los argumentos posteriores si los anteriores no son falsos, en un lenguaje con funciones estrictas de llamada por valor. En cambio, esto se comporta de manera similar a if / then / else.
Los operadores menos comunes incluyen:
- Operador de coma :
e, f
- Operador de desreferencia :
*p
y operador de dirección de:&x
- ?: o operador ternario:
number = spell_out_numbers ? "forty-two" : 42
- Operador de Elvis :
x ?: y
- Operador de Elvis :
- Operador coalescente nulo :
x ?? y
- Operador de nave espacial (para comparación de tres vías ):
x <=> y
Compilacion
Un compilador puede implementar operadores y funciones con llamadas a subrutinas o con código en línea . Algunos operadores integrados soportados por un lenguaje tienen un mapeo directo a una pequeña cantidad de instrucciones que se encuentran comúnmente en las unidades centrales de procesamiento , aunque otros ( por ejemplo, '+' usado para expresar la concatenación de cadenas ) pueden tener implementaciones complicadas.
Sobrecarga del operador
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 El operador + se usa tanto para la suma de números como para la concatenación de cadenas). Se dice que tal operador está sobrecargado . En los lenguajes que admiten la sobrecarga de operadores por parte del programador (como C ++ ) pero tienen un conjunto limitado de operadores, la sobrecarga de operadores se utiliza a menudo para definir usos personalizados para los operadores.
En el ejemplo IF ORDER_DATE > "12/31/2011" AND ORDER_DATE < "01/01/2013" THEN CONTINUE ELSE STOP
, los operadores son: ">" (mayor que), "Y" y "<" (menor que).
Coacción de operando
Algunos lenguajes también permiten que los operandos de un operador sean convertidos implícitamente, o forzados , a tipos de datos adecuados para que ocurra la operación. Por ejemplo, en Perl las reglas de coerció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 pueda tener lugar la adición. Además, 12
es un número entero y 3.14
es un número de punto fijo o flotante (un número que tiene un lugar decimal) por lo que el número entero se convierte en 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 coacciones en un lenguaje, el programador debe conocer las reglas específicas con respecto a los tipos de operandos y el tipo de resultado de la operación para evitar errores sutiles de programación.
Funciones del operador en lenguajes de programación
La siguiente tabla muestra las características del operador en varios lenguajes de programación:
Lenguaje de programación | Símbolos de operador no alfanuméricos | Símbolos de operador alfanuméricos | Prefijo | Infijo | Sufijo | Precedencia | Asociatividad | Sobrecarga | Sobrecarga definida por el programador | Símbolos de operador definidos por el programador |
---|---|---|---|---|---|---|---|---|---|---|
ALGOL 68 | + * ** * /%% *% × - + <<=> => = / = & -: = +: = *: = /: =%: =% *: = + =:: =:: / =: (Todos los operadores tienen equivalentes alfanuméricos en negrita , consulte la siguiente columna. Algunos tienen equivalentes no ASCII , consulte a continuación). ¬ + × ⊥ ↑ ↓ ⌊ ⌈ × ÷ ÷ × ÷ * □ ≤ ≥ ≠ ∧ ∨ ×: = ÷: = ÷ ×: = ÷ *: =% ×: =: ≠: | no abs arg bin entier leng nivel impar repr ronda acortar i SHL SHR arriba abajo lwb UPB lt le ge gt eq ne y o sobre mod elem minusab plusab timesab divab overab modab plusto es decir isnt | sí | sí | No | Sí (los operadores de prefijo siempre tienen prioridad 10) | Los operadores de infijo son asociativos por la izquierda, los operadores de prefijo son asociativos por la derecha | sí | sí | sí |
APL | + - × ÷ ⌈ ⌊ * ⍟ | ! ○ ~ ∨ ∧ ⍱ ⍲ <≤ = ≥> ≠. @ ≡ ≢ ⍴, ⍪ ⍳ ↑ ↓? ⍒ ⍋ ⍉ ⌽ ⊖ ∊ ⊥ ⊤ ⍎ ⍕ ⌹ ⊂ ⊃ ∪ ∩ ⍷ ⌷ ∘ → ← / ⌿ \ ⍀ ¨ ⍣ & ⍨ ⌶ ⊆ ⊣ ⊢ ⍠ ⍤ ⌸ ⌺ ⍸ | Los símbolos alfanuméricos necesitan una ⎕ antes de la palabra clave | Sí (solo funciones de primer orden) | sí | Sí (solo funciones de orden superior) | Las funciones de orden superior preceden a las funciones de primer orden | Las funciones de orden superior son asociativas por la izquierda, las funciones de primer orden son asociativas por la derecha | sí | sí | Sí (solo alfanumérico) |
C | () [] ->. ! ~ ++ - + - * & /% << >> <<=>> = ==! = ^ | && || ?: = + = - = * = / =% = & = ^ = | tamaño de | sí | sí | sí | sí | sí | sí | No | No |
C ++ ( más ) | sizeof typeid nuevo eliminar lanzar decltype static_cast dynamic cast reinterpret_cast const_cast | sí | sí | sí | sí | sí | sí | sí | No | |
C # ( más ) | Igual que C / C ++, junto con ?. ? [] ?? | sizeof nameof new stackalloc await throw marcado sin marcar es como delegado predeterminado verdadero falso LINQ :desde seleccione dónde grupo ... por grupo ... por ... en unirse ... en ... en ... igual a unirse ... en ... en ... es igual a ... en orden por orden por. ... descendiendo Roslyn -sólo:__makeref __refvalue __reftype | sí | sí | sí | sí | sí | sí | sí | No |
Java | Igual que C / C ++ | nueva instancia de lanzamiento | sí | sí | sí | sí | sí | sí | No | No |
Eiffel | [] + - * / // = / = | no y o implica "y luego" "o si no" | sí | sí | No | sí | sí | No | sí | sí |
Haskell | + - * / ^ ^^ ** == / => <> = <= && || >> = >> $ $! . ++ !! : Muchos más en bibliotecas comunes | El nombre de la función se debe poner entre comillas invertidas | sí | sí | No | sí | sí | Sí, usando clases de tipo | sí | |
Pascal | * / + - = <> <> <=> =: = | No div mod y o en | sí | sí | No | sí | sí | sí | No | No |
Perl | -> ++ - **! ~ \ + -. = ~! ~ * /% <> <=> = ==! = <=> ~~ & | ^ && || ' | imprimir ordenar chmod chdir rand y o no xor lt gt le ge eq ne cmp x | sí | sí | sí | sí | sí | sí | sí | No |
Raku | ++ - **! ~ ~~ * / + -. <> <=> = ==! = <=> & | ^ && || // [7] | imprimir ordenar chmod chdir rand y o no xor lt gt le ge eq ne leg cmp x xx | sí | sí | sí | sí | sí | sí | sí | Sí [8] |
PHP | [] ** ++ - ~ @! [9] * /% + -. << >> <<=>> = ==! = ===! == <> <=> & ^ | && || ?? ?: = + = - = * = ** = / =. =% = & = | = ^ = << = >> = | clon nueva impresión desarmado eco isset instanceof y o XOR | sí | sí | sí | sí | sí | No | No | No |
PL / I | () -> + - * / **> ¬>> = = ¬ = <= <¬ <¬ & | || | sí | sí | No | sí | sí | No | No | No | |
Prólogo | : -? -; ,. = .. = \ = <= <> => == \ == - + / * | espía nospy no es mod | sí | sí | sí | sí | sí | No | No | sí |
Semilla7 | {} [] -> **! + - * / << >> &> <| = <>>> = <<= <&: = +: = -: = *: = /: = <<: = >>: = &: = @: = | conv varConv parse conj div rem mdiv mod times mult in not y o digits lpad rpad lpad0 | sí | sí | sí | sí | sí | sí | sí | sí |
Charla | (sí, hasta dos caracteres [10] ) | Los símbolos alfanuméricos necesitan dos puntos después de la palabra clave | No | sí | sí | No | No | sí | sí | sí |
Rápido | Cualquier cadena de símbolo Unicode excepto . , incluyendo ! ~ + - * /% = + = - = * = / =% & + & - & * = & + = & - = & * && || << >> & | ^ ==! = <<=>> = ?? ... .. < en la biblioteca estándar | es como como? | sí | sí | sí | Sí (definido como orden parcial en grupos de precedencia) | Sí (definido como parte de grupos de precedencia) | sí | sí | sí |
Visual Basic .NET | (). ! ? ()?. ?! + - * / \ & << >> <<=>> = ^ <> = + = - = * = / = \ = & = ^ = << = >> = | New Await Mod Like IsNot Not Y AndAlso Or OrElse Xor If (..., ...) If (..., ..., ...) GetXmlNamespace (...) GetType (...) NameOf ( ...) TypeOf ... Is TypeOf ... IsNot DirectCast (..., ...) TryCast (..., ...) CType (..., ...) CBool (...) CByte (...) CChar (...) CDate (...) CDec (...) CDbl (...) CInt (...) CLng (...) CObj (...) CSByte ( ...) CShort (...) CSng (...) CStr (...) CUInt (...) CULng (...) CUShort (...) LINQ :Desde Agregado ... a Seleccionar Distinto Donde | sí | sí | sí | sí | sí | sí | sí | No |
Ver también
- Operador relacional
Notas
- ^ Por el contrario, un operador asociativo a la derecha con su argumento correcto, aunque esto es más raro.
- ^ La introducción de un nuevo operador cambia la especificación léxica del idioma, lo que cambia el análisis léxico . La aridad y la precedencia del operador es parte de la sintaxis de la frase del idioma, lo que cambia el análisis a nivel de frase. Por ejemplo, agregar un operador
@
requiere lex y tokenizar este carácter, y la estructura de la frase (árbol de sintaxis) depende de la aridad y la precedencia de este operador.
Referencias
- ^ "Formularios de entrada del operador: documentación de Wolfram Language" . reference.wolfram.com .
- ^ "Manual de Maxima 5.42.0: 7. Operadores" . maxima.sourceforge.net .
- ^ "Operadores Prefijos, Postfijos y Circunfijos" . mythryl.org .
- ^ "Operadores" . doc.perl6.org .
- ^ "SWI-Prolog - op / 3" . www.swi-prolog.org .
- ^ "Declare un operador" . seed7.sourceforge.net .
- ^ "Operadores" . docs.perl6.org .
- ^ "Funciones" . docs.perl6.org .
- ^ "PHP: Operadores de control de errores - Manual" . php.net .
- ^ Goldberg, Adele. "Smalltalk-80: El lenguaje y su implementación, p. 27, ISBN 0-201-11371-6" (PDF) .