Share via


Comportamiento del operador

En esta sección se define el comportamiento de los distintos operadores de M.

Prioridad de los operadores

Cuando una expresión contiene varios operadores, su precedencia controla el orden en el que se evalúan los operadores individuales. Por ejemplo, la expresión x + y * z se evalúa como x + (y * z) porque el operador * tiene mayor precedencia que el operador binario +. La precedencia de un operador se establece mediante la definición de su producción gramatical asociada. Por ejemplo, una instancia de additive-expression consta de una secuencia de multiplicative-expression separadas por los operadores + o -, por lo que se asigna a los operadores + y - una menor precedencia que la de los operadores * y /.

La producción parenthesized-expression se puede usar para cambiar el orden de precedencia predeterminado.

parenthesized-expression:
      (expression)

Por ejemplo:

1 + 2 * 3       // 7 
(1 + 2) * 3     // 9

En la tabla siguiente se resumen los operadores de M y se enumeran las categorías de operador en orden de prioridad, de mayor a menor. Los operadores de la misma categoría tienen la misma precedencia.

Categoría Expresión Descripción
Principal i
@i
Expresión de identificador
(x) Expresión entre paréntesis
x[i] Lookup
x{y} Acceso a elementos
x(...) Invocación de funciones
{x, y, ...} Inicialización de listas
[ i = x, ... ] Inicialización de registros
... No implementado
Unario +x Identidad
-x Negación
notx Negación lógica
Metadatos xmetay Asociación de metadatos
Multiplicativa x * y Multiplicación
x / y División
Aditiva x + y Suma
x - y Resta
Relacional x< y Menor que
x > y Mayor que
x<= y Menor o igual que
x >= y Mayor o igual que
Igualdad x = y Igual
x<> y No igual a
Aserción de tipos xasy Es compatible con tipos primitivos que aceptan valores NULL o error
Conformidad de tipos xisy Prueba la compatibilidad del tipo primitivo que acepta valores NULL
Y lógico xandy Conjunción de cortocircuito
O lógico xory Disyunción de cortocircuito
Coalesce x??y Operador de fusión de NULL

Operadores y metadatos

Cada valor tiene un valor de registro asociado que puede contener información adicional sobre el valor. Este registro se conoce como el registro de metadatos de un valor. Un registro de metadatos se puede asociar a cualquier tipo de valor, incluso null. El resultado de este tipo de asociación es un nuevo valor con los metadatos especificados.

Un registro de metadatos es simplemente un registro normal y puede contener todos los campos y valores de un registro normal, y a su vez puede ser un registro de metadatos. La asociación de un registro de metadatos con un valor es "no intrusiva". No cambia el comportamiento del valor en las evaluaciones, excepto el de las que inspeccionan registros de metadatos de forma explícita.

Cada valor tiene un registro de metadatos predeterminado, incluso si no se ha especificado ninguno. El registro de metadatos predeterminado está vacío. En los ejemplos siguientes se muestra el acceso al registro de metadatos de un valor de texto mediante la función de la biblioteca estándar Value.Metadata:

Value.Metadata( "Mozart" )   // []

Generalmente, los registros de metadatos no se conservan cuando un valor se usa con un operador o una función que crea un valor. Por ejemplo, si se concatenan dos valores de texto con el operador &, los metadatos del valor de texto resultante es el registro vacío []. Las expresiones siguientes son equivalentes:

"Amadeus " & ("Mozart" meta [ Rating = 5 ])  
"Amadeus " & "Mozart"

Se pueden usar las funciones de biblioteca estándar Value.RemoveMetadata y Value.ReplaceMetadata para quitar todos los metadatos de un valor y para reemplazarlos (en lugar de combinarlos en metadatos que ya existan).

El único operador que devuelve resultados que contienen metadatos es el operador meta.

Operadores recursivos estructuralmente

Los valores pueden ser cíclicos. Por ejemplo:

let l = {0, @l} in l
// {0, {0, {0, ... }}}
[A={B}, B={A}]
// [A = {{ ... }}, B = {{ ... }}]

Para controlar los valores cíclicos en M, la construcción de registros, listas y tablas es diferida. Un intento de construir un valor cíclico que no se beneficie de los valores estructurados intercalados en diferido produce un error:

[A=B, B=A] 
// [A = Error.Record("Expression.Error", 
//         "A cyclic reference was encountered during evaluation"), 
//  B = Error.Record("Expression.Error", 
//         "A cyclic reference was encountered during evaluation"), 
// ]

Algunos operadores de M se definen mediante la recursión estructural. Por ejemplo, la igualdad de registros y listas se define mediante la igualdad conjunta de los campos de registro y listas de elementos correspondientes, respectivamente.

Para los valores que no son cíclicos, la aplicación de recursión estructural genera una expansión finita del valor: los valores anidados compartidos se recorren repetidamente, pero el proceso de recursión siempre finaliza.

Un valor cíclico tiene una expansión infinita cuando se aplica la recursión estructural. En la semántica de M no hay disposiciones especiales para estas expansiones infinitas; un intento de comparar la igualdad de los valores cíclicos, por ejemplo, se quedará sin recursos y finalizará de forma excepcional.

Operadores de selección y proyección

Los operadores de selección y proyección permiten extraer datos de valores de lista y de registro.

Acceso a elementos

Se puede seleccionar un valor de una lista o una tabla en función de su posición de base cero dentro de esa lista o tabla mediante item-access-expression.

item-access-expression:
      item-selection
      optional-item-selection
item-selection:
      primary-expression
{item-selector}
optional-item-selection:
      primary-expression
{item-selector} ?
item-selector:
      expression

item-access-expressionx{y} devuelve lo siguiente:

  • En el caso de una lista x y un número y, el elemento de lista x en la posición y. El primer elemento de una lista se considera que tiene un índice ordinal de cero. Si la posición solicitada no existe en la lista, se produce un error.

  • En el caso de una tabla x y un número y, la fila de la tabla x en la posición y. La primera fila de una tabla se considera que tiene un índice ordinal de cero. Si la posición solicitada no existe en la tabla, se produce un error.

  • En el caso de una tabla x y un registro y, la fila de la tabla x que coincide con los valores de campo de registro y para los campos con nombres de campo que coinciden con los nombres de columna de tabla correspondientes. Si no hay ninguna fila coincidente única en la tabla, se produce un error.

Por ejemplo:

{"a","b","c"}{0}                        // "a" 
{1, [A=2], 3}{1}                        // [A=2] 
{true, false}{2}                        // error 
#table({"A","B"},{{0,1},{2,1}}){0}      // [A=0,B=1] 
#table({"A","B"},{{0,1},{2,1}}){[A=2]}  // [A=2,B=1]  
#table({"A","B"},{{0,1},{2,1}}){[B=3]}  // error 
#table({"A","B"},{{0,1},{2,1}}){[B=1]}  // error

item-access-expression también admite el formato x{y}?, que devuelve null cuando la posición (o coincidencia) y no existe en la lista o la tabla x. Si hay varias coincidencias para y, se producirá un error.

Por ejemplo:

{"a","b","c"}{0}?                       // "a" 
{1, [A=2], 3}{1}?                       // [A=2] 
{true, false}{2}?                       // null 
#table({"A","B"},{{0,1},{2,1}}){0}?     // [A=0,B=1] 
#table({"A","B"},{{0,1},{2,1}}){[A=2]}? // [A=2,B=1]  
#table({"A","B"},{{0,1},{2,1}}){[B=3]}? // null 
#table({"A","B"},{{0,1},{2,1}}){[B=1]}? // error

El acceso a los elementos no fuerza la evaluación de elementos de lista o tabla distintos del elemento al que se accede. Por ejemplo:

{ error "a", 1, error "c"}{1}  // 1 
{ error "a", error "b"}{1}     // error "b"

Cuando se evalúa el operador de acceso a elementos x{y}, sucede lo siguiente:

  • Se propagan los errores generados durante la evaluación de expresiones x o y.

  • La expresión x genera un valor de lista o tabla.

  • La expresión y genera un valor numérico o, si x genera un valor de tabla, un valor de registro.

  • Si y genera un valor numérico y el valor de y es negativo, se genera un error con el código de motivo "Expression.Error".

  • Si y genera un valor numérico y el valor de y es mayor o igual que el recuento de x, se genera un error con el código de motivo "Expression.Error", a menos que se use el formato de operador opcional x{y}?, en cuyo caso se devuelve el valor null.

  • Si x genera un valor de tabla y y genera un valor de registro, y no hay coincidencias con y en x, se genera un error con el código de motivo "Expression.Error", a menos que se use el formato de operador opcional x{y}?, en cuyo caso se devuelve el valor null.

  • Si x genera un valor de tabla y y genera un valor de registro, y hay varias coincidencias con y en x, se genera un error con el código de motivo "Expression.Error".

No se evalúa ningún elemento en x que no sea el que se encuentra en la posición y durante el proceso de selección del elemento. (Para las listas o tablas de streaming, se omiten los elementos o las filas anteriores a la posición y, lo que puede provocar su evaluación, en función del origen de la lista o de la tabla).

Acceso a campos

field-access-expression se usa para seleccionar un valor de un registro o para proyectar un registro o una tabla en otro registro u otra tabla uno con menos campos o columnas, respectivamente.

field-access-expression:
      field-selection
      implicit-target-field-selection
      proyección
      implicit-target-projection
field-selection:
      primary-expression field-selector
field-selector:
      required-field-selector
      optional-field-selector
required-field-selector:

      [field-name]
optional-field-selector:
      [field-name] ?
field-name:
      generalized-identifier
      quoted-identifier
implicit-target-field-selection:
      field-selector
projection:
      primary-expression required-projection
      primary-expression optional-projection
required-projection:

      [required-selector-list]
optional-projection:
      [required-selector-list] ?
required-selector-list:
      required-field-selector
      required-selector-list
,required-field-selector
implicit-target-projection:
      required-projection
      optional-projection

La forma más sencilla de acceso a campos es la selección de campos obligatorios. Usa el operador x[y] para buscar un campo en un registro por nombre de campo. Si un campo y no existe en x, se produce un error. El formato x[y]? se usa para realizar la selección de campos opcionales y devuelve null si el campo solicitado no existe en el registro.

Por ejemplo:

[A=1,B=2][B]       // 2 
[A=1,B=2][C]       // error 
[A=1,B=2][C]?      // null

El acceso colectivo de varios campos es compatible con los operadores para la proyección de registros obligatorios y la proyección de registros opcionales. El operador x[[y1],[y2],...] proyecta el registro en un registro nuevo con menos campos (seleccionados por y1, y2, ...). Si un campo seleccionado no existe, se produce un error. El operador x[[y1],[y2],...] proyecta el registro en un registro nuevo con los campos seleccionados por y1, y2, ...; si falta un campo, se usa null en su lugar. Por ejemplo:

[A=1,B=2][[B]]           // [B=2] 
[A=1,B=2][[C]]           // error 
[A=1,B=2][[B],[C]]?      // [B=2,C=null]

Se admiten los formatos [y] y [y]? como una referencia abreviada al identificador _ (carácter de subrayado). Las dos expresiones siguientes son equivalentes:

[A]                 
_[A]

En el ejemplo siguiente se muestra el formato abreviado del acceso a campos:

let _ = [A=1,B=2] in [A] //1

También se admiten los formatos [[y1],[y2],...] y [[y1],[y2],...]? como abreviatura y las dos expresiones siguientes también son equivalentes:

[[A],[B]]                 
_[[A],[B]]

El formato abreviado es especialmente útil si se combina con la abreviatura each, una manera de introducir una función de un único parámetro denominado _ (para obtener más información, vea Declaraciones simplificadas). Juntas, las dos abreviaturas simplifican las expresiones funcionales de orden superior comunes:

List.Select( {[a=1, b=1], [a=2, b=4]}, each [a] = [b]) 
// {[a=1, b=1]}

La expresión anterior es equivalente a la siguiente, más extensa y de aspecto críptico:

List.Select( {[a=1, b=1], [a=2, b=4]}, (_) => _[a] = _[b]) 
// {[a=1, b=1]}

El acceso a campos no fuerza la evaluación de otros distintos al que se accede. Por ejemplo:

[A=error "a", B=1, C=error "c"][B]  // 1 
[A=error "a", B=error "b"][B]       // error "b"

Cuando se evalúa un operador de acceso a campos x[y], x[y]?, x[[y]] o x[[y]]?, sucede lo siguiente:

  • Se propagan los errores generados durante la evaluación de la expresión x.

  • Los errores que se producen al evaluar el campo y están asociados permanentemente al campo y y luego se propagan. Cualquier acceso futuro al campo y producirá el mismo error.

  • La expresión x genera un valor de registro o tabla, o bien se produce un error.

  • Si el identificador y menciona a un campo que no existe en x, se genera un error con el código de motivo "Expression.Error", a menos que se use el formato de operador opcional ...?, en cuyo caso se devuelve el valor null.

En el proceso de acceso a campos, no se evalúa ningún campo de x que no sea el que menciona y.

Operador de metadatos

El registro de metadatos de un valor se modifica mediante el operador meta (x meta y).

metadata-expression:
      unary-expression
      unary-expression
metaunary-expression

En el ejemplo siguiente se crea un valor de texto con un registro de metadatos mediante el operador meta y, después, se accede al registro de metadatos del valor resultante mediante Value.Metadata:

Value.Metadata( "Mozart" meta [ Rating = 5 ] ) 
// [Rating = 5 ]
Value.Metadata( "Mozart" meta [ Rating = 5 ] )[Rating] 
// 5

Cuando se aplica el operador de combinación de metadatos x meta y, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones x o y.

  • La expresión y debe ser un registro, o bien se produce un error con el código de motivo "Expression.Error".

  • El registro de metadatos resultante es el de x combinado con y. (Para obtener la semántica de la combinación de registros, vea Combinación de registros).

  • El valor resultante es el de la expresión x, sin sus metadatos, con el registro de metadatos recién calculado adjunto.

Se pueden usar las funciones de biblioteca estándar Value.RemoveMetadata y Value.ReplaceMetadata para quitar todos los metadatos de un valor y para reemplazarlos (en lugar de combinarlos en metadatos que ya existan). Las expresiones siguientes son equivalentes:

x meta y  
Value.ReplaceMetadata(x, Value.Metadata(x) & y) 
Value.RemoveMetadata(x) meta (Value.Metadata(x) & y)

Operadores de igualdad

El operador de igualdad= se usa para determinar si dos valores son iguales. El operador de desigualdad<> se usa para determinar si dos valores no son iguales.

equality-expression:
      relational-expression
      relational-expression
=equality-expression
      relational-expression
<>equality-expression

Por ejemplo:

1 = 1            // true 
1 = 2            // false 
1 <> 1           // false 
1 <> 2           // true 
null = true      // false 
null = null      // true

Los metadatos no forman parte de la comparación de igualdad o desigualdad. Por ejemplo:

(1 meta [ a = 1 ]) = (1 meta [ a = 2 ]) // true 
(1 meta [ a = 1 ]) = 1                  // true

Cuando se aplican los operadores de igualdad x = y y x <> y, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones x o y.

  • El operador = tiene un resultado de true si los valores son iguales y de false en caso contrario.

  • El operador <> tiene un resultado de false si los valores son iguales y de true en caso contrario.

  • Los registros de metadatos no se incluyen en la comparación.

  • Si los valores generados al evaluar las expresiones x e y no tienen el mismo tipo de valor, los valores no son iguales.

  • Si los valores generados al evaluar las expresiones x e y son el mismo tipo de valor, hay reglas específicas para determinar si son iguales, como se define a continuación.

  • Lo siguiente se cumple siempre:

    (x = y) = not (x <> y)

Los operadores de igualdad se definen para los tipos siguientes:

  • El valor null solo es igual a sí mismo.
    null = null    // true 
    null = true    // false 
    null = false   // false
  • Los valores lógicos true y false solo son iguales a sí mismos. Por ejemplo:
    true = true      // true 
    false = false    // true 
    true = false     // false 
    true = 1         // false
  • Los números se comparan con la precisión especificada:

    • Si uno es #nan, los números no son los mismos.

    • Si ninguno es #nan, los números se comparan mediante una comparación de bits del valor numérico.

    • #nan es el único valor que no es igual a sí mismo.

      Por ejemplo:

        1 = 1,              // true 
        1.0 = 1             // true 
        2 = 1               // false 
        #nan = #nan         // false 
        #nan <> #nan        // true
  • Dos duraciones son iguales si representan el mismo número de tics de 100 nanosegundos.

  • Dos horas son iguales si las magnitudes de sus partes (hora, minuto, segundo) son iguales.

  • Dos fechas son iguales si las magnitudes de sus partes (año, mes, día) son iguales.

  • Dos valores de fecha y hora son iguales si las magnitudes de sus partes (año, mes, día, hora, minuto, segundo) son iguales.

  • Dos valores de fecha, hora y zona horaria son iguales si los valores de fecha y hora UTC correspondientes son iguales. Para llegar a la fecha y hora UTC correspondiente, el desplazamiento de horas/minutos se resta del componente de fecha y hora del valor de fecha, hora y zona horaria.

  • Dos valores de texto son iguales si al usar una comparación ordinal, con distinción de mayúsculas y minúsculas y sin distinción de referencia cultural tienen la misma longitud y caracteres iguales en las posiciones correspondientes.

  • Dos valores de lista son iguales si se cumple todo lo siguiente:

    • Las dos listas contienen el mismo número de elementos.

    • Los valores de cada elemento posicionalmente correspondiente en las listas son iguales. Esto significa que las listas no solo deben contener elementos iguales, sino que deben estar en el mismo orden.

      Por ejemplo:

        {1, 2} = {1, 2}     // true 
        {2, 1} = {1, 2}     // false 
        {1, 2, 3} = {1, 2}  // false
      
  • Dos registros son iguales si se cumple todo lo siguiente:

    • El número de campos es el mismo.

    • Cada nombre de campo de un registro también está presente en el otro registro.

    • El valor de cada campo de un registro es igual al campo con el mismo nombre del otro registro.

      Por ejemplo:

        [ A = 1, B = 2 ] = [ A = 1, B = 2 ]        // true 
        [ B = 2, A = 1 ] = [ A = 1, B = 2 ]        // true 
        [ A = 1, B = 2, C = 3 ] = [ A = 1, B = 2 ] // false 
        [ A = 1 ] = [ A = 1, B = 2 ]               // false
      
  • Dos tablas son iguales si se cumple todo lo siguiente:

    • El número de columnas es el mismo.

    • Cada nombre de columna de una tabla también está presente en la otra.

    • El número de filas es el mismo.

    • Cada fila tiene los mismos valores en las celdas correspondientes.

      Por ejemplo:

        #table({"A","B"},{{1,2}}) = #table({"A","B"},{{1,2}}) // true 
        #table({"A","B"},{{1,2}}) = #table({"X","Y"},{{1,2}}) // false 
        #table({"A","B"},{{1,2}}) = #table({"B","A"},{{2,1}}) // true
      
  • Un valor de función es igual a sí mismo, pero puede o no ser igual a otro valor de función. Si dos valores de función se consideran iguales, se comportarán de la misma manera cuando se invocan.

    Dos valores de función dados siempre tendrán la misma relación de igualdad.

  • Un valor de tipo es igual a sí mismo, pero puede o no ser igual a otro valor de tipo. Si dos valores de tipo se consideran iguales, se comportarán de la misma manera cuando se consulta su compatibilidad.

    Dos valores de tipo dados siempre tendrán la misma relación de igualdad.

Operadores relacionales

Los operadores <, >, <= y >= se denominan operadores relacionales.

relational-expression:
      additive-expression
      additive-expression
<relational-expression
      additive-expression
>relational-expression
      additive-expression
<= _relational-expression
      additive-expression >=relational-expression

Estos operadores se usan para determinar la relación de ordenación relativa entre dos valores, como se muestra en la tabla siguiente:

Operación Resultado
x < y true si x es menor que y, false en caso contrario
x > y true si x es mayor que y, false en caso contrario
x <= y true si x es menor o igual que y, false en caso contrario
x >= y true si x es mayor o igual que y, false en caso contrario

Por ejemplo:

0 <= 1            // true 
null < 1          // null 
null <= null      // null 
"ab" < "abc"      // true 
#nan >= #nan      // false  
#nan <= #nan      // false

Cuando se evalúa una expresión que contiene los operadores relacionales, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones de operando x o y.

  • Los valores generados al evaluar las expresiones x e y deben ser de binario, fecha, fecha y hora, fecha, hora y zona horaria, duración, lógicos, null, texto o de hora. De lo contrario, se genera un error con el código de motivo "Expression.Error".

  • Ambos operandos deben ser el mismo tipo de valor o null. De lo contrario, se genera un error con el código de motivo "Expression.Error".

  • Si uno o los dos operandos son null, el resultado es el valor null.

  • Dos archivos binarios se comparan byte por byte.

  • Para comparar dos fechas se comparan sus partes de año y, si son iguales, sus partes de mes y, si son iguales, sus partes de día.

  • Para comparar dos valores de fecha y hora se comparan sus partes de año y, si son iguales, sus partes de mes y, si son iguales, sus partes de día y, si son iguales, sus partes de hora y, si son iguales, sus partes de minuto y, si son iguales, sus partes de segundo.

  • Para comparar dos valores de fecha, hora y zona horaria, se normalizan a la hora UTC restando su desplazamiento por hora/minuto y comparando después sus componentes de fecha y hora.

  • Dos duraciones se comparan según el número total de tics de 100 nanosegundos que representan.

  • Se comparan dos elementos lógicos de forma que true se considera mayor que false.

  • Dos números x e y se comparan según las reglas del estándar IEEE 754:

    • Si alguno de los operandos es #nan, el resultado es false para todos los operadores relacionales.

    • Cuando ninguno de los operandos es #nan, los operadores comparan los valores de los dos operandos de punto flotante con respecto a la ordenación -∞ < -max < ... < -min < -0.0 = +0.0 < +min < ... < +max < +∞, donde "min" y "max" son los valores finitos positivos más pequeños y más grandes que se pueden representar. En M, -∞ y +∞ se denominan "-#infinity" y "#infinity".

      Los efectos importantes de este orden son:

      • Los ceros negativos y positivos se consideran iguales.

      • Un valor -#infinity se considera menor que el resto de los valores numéricos, pero es igual a otro -#infinity.

      • Un valor #infinity se considera mayor que el resto de los valores numéricos, pero es igual a otro #infinity.

  • Se comparan dos textos mediante una comparación sin distinción entre mayúsculas y minúsculas, sin distinción entre mayúsculas y minúsculas.

  • Para comparar dos horas se comparan sus partes de hora y, si son iguales, sus partes de minuto y, si son iguales, sus partes de segundo.

Operadores lógicos condicionales

Los operadores and y or se denominan operadores lógicos condicionales.

logical-or-expression:
      logical-and-expression
logical-and-expression
orlogical-or-expression
logical-and-expression:
      is-expression
      is-expression
andlogical-and-expression

El operador or devuelve true cuando al menos uno de sus operandos es true. El operando derecho se evalúa solo si el operando izquierdo no es true.

El operador and devuelve false cuando al menos uno de sus operandos es false. El operando derecho se evalúa solo si el operando izquierdo no es false.

A continuación se muestran las tablas de verdad para los operadores or y and, con el resultado de evaluar la expresión de operando de la izquierda en el eje vertical y el de evaluar la expresión de operando de la derecha en el horizontal.

and true false null error
true true false null error
false false false false false
null null false null error
error error error error error
or true false null error
or true false null error
true true true true true
false true false null error
null true null null error
error error error error error

Cuando se evalúa una expresión que contiene operadores lógicos condicionales, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones x o y.

  • Los operadores lógicos condicionales se definen antes que los tipos logical y null. Si los valores de operando no son de esos tipos, se genera un error con el código de motivo "Expression.Error".

  • El resultado es un valor lógico.

  • En la expresión x o y, se evaluará la expresión y solo si x no se evalúa como true.

  • En la expresión x e y, se evaluará la expresión y solo si x no se evalúa como false.

Las dos últimas propiedades proporcionan a los operadores lógicos condicionales su calificación de "condicionales"; las propiedades también se denominan "cortocircuito". Estas propiedades son útiles para escribir predicados protegidos. Por ejemplo, las expresiones siguientes son equivalentes:

d <> 0 and n/d > 1 if d <> 0 then n/d > 1 else false

Operadores aritméticos

Los operadores +, -, * y / son los operadores aritméticos.

additive-expression:
      multiplicative-expression
      additive-expression
+multiplicative-expression
      additive-expression
-multiplicative-expression
expresión-multiplicativa:
      metadata- expression
      multiplicative-expression
*metadata-expression
      multiplicative-expression
/metadata-expression

Precisión

En M, los números se almacenan con diversas representaciones para conservar tanta información como sea posible sobre los números procedentes de diversos orígenes. Los números solo se convierten de una representación a otra según sea necesario para los operadores que se les aplica. En M se admiten dos precisiones:

Precisión Semántica
Precision.Decimal Representación decimal de 128 bits con un intervalo de ±1,0 x 10-28 a ±7,9 x 1028 and 28-29 dígitos significativos.
Precision.Double Representación científica con mantisa y exponente; se ajusta al estándar aritmético IEEE 754 de doble precisión de 64 bits IEEE 754-2008.

Para realizar operaciones aritméticas, se elige una precisión, se convierten los dos operandos a esa precisión (si es necesario), después se realiza la operación real y, por último, se devuelve un número con la precisión elegida.

Los operadores aritméticos integrados (+, -, *, /) usan precisión doble. Las funciones de la biblioteca estándar (Value.Add, Value.Subtract, Value.Multiply, Value.Divide) se pueden usar para solicitar estas operaciones con un modelo de precisión específico.

  • El desbordamiento numérico no es posible: #infinity o -#infinity representan valores de magnitudes demasiado grandes para representarlos.

  • El subdesbordamiento numérico no es posible: 0 y -0 representan valores de magnitudes demasiado pequeñas para representarlos.

  • El valor especial de IEEE 754 #nan (NaN: No es un número) se usa para cubrir los casos aritméticos no válidos, como la división de cero por cero.

  • Para realizar la conversión de precisión decimal a doble se redondean los números decimales al valor doble equivalente más próximo.

  • Para realizar la conversión de precisión doble a decimal se redondean los números dobles al valor decimal equivalente más cercano y, si es necesario, se desborda a valores #infinity o -#infinity.

Operador de suma

La interpretación del operador de suma (x + y) depende del tipo de valor de las expresiones x e y evaluadas, como se indica a continuación:

x y Resultado Interpretación
type number type number type number Suma numérica
type number null null
null type number null
type duration type duration type duration Suma numérica de magnitudes
type duration null null
null type duration null
typedatetime type duration typedatetime Desplazamiento de fecha y hora según la duración
type duration typedatetime typedatetime
typedatetime null null
null typedatetime null

En la tabla, typedatetime representa type date, type datetime, type datetimezone o type time. Al sumar una duración y un valor de algún tipo datetime, el valor resultante es del mismo tipo.

Para otras combinaciones de valores que no sean las enumeradas en la tabla, se genera un error con el código de motivo "Expression.Error". Cada combinación se describe en las secciones siguientes.

Se propagan los errores que se producen al evaluar cualquiera de los operandos.

Suma numérica

La suma de dos números se calcula mediante el operador de suma, lo que genera un número.

Por ejemplo:

1 + 1             // 2 
#nan + #infinity  // #nan

El operador de suma + en números usa la precisión doble; la función de la biblioteca estándar Value.Add se puede usar para especificar la precisión decimal. Al calcular una suma de números sucede lo siguiente:

  • La suma en precisión doble se calcula de acuerdo a las reglas aritméticas de IEEE 754 de precisión doble binaria de 64 bits IEEE 754-2008. En la tabla siguiente se enumeran los resultados de todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y NaN. En la tabla, x e y son valores finitos distintos de cero y z es el resultado de x + y. Si x e y tienen la misma magnitud pero signos opuestos, z es cero positivo. Si x + y es demasiado grande para representarse en el tipo de destino, z es un infinito con el mismo signo que x + y.

    + y +0 -0 +∞ -∞ NaN
    x z x x +∞ -∞ NaN
    +0 y +0 +0 +∞ -∞ NaN
    -0 y +0 -0 +∞ -∞ NaN
    +∞ +∞ +∞ +∞ +∞ NaN NaN
    -∞ -∞ -∞ -∞ NaN -∞ NaN
    NaN NaN NaN NaN NaN NaN NaN
  • La suma en precisión decimal se calcula sin perder precisión. La escala del resultado es la mayor de las escalas de los dos operandos.

Suma de las duraciones

La suma de dos duraciones es la duración que representa la suma del número de tics de 100 nanosegundos representados por las duraciones. Por ejemplo:

#duration(2,1,0,15.1) + #duration(0,1,30,45.3) 
// #duration(2, 2, 31, 0.4)

Desplazamiento de fecha y hora según la duración

Se puede sumar un valor datetimex y una duración y con x + y para calcular un nuevo valor datetime cuya distancia con respecto a x en una escala de tiempo lineal sea exactamente la magnitud de y. Aquí, datetime equivale a Date, DateTime, DateTimeZone o Time, y un resultado no NULL será del mismo tipo. El desplazamiento de fecha y hora por duración se puede calcular de la manera siguiente:

  • Si se especifican los días de la fecha y hora desde el valor de época, se construye un nuevo objeto datetime con los elementos de información siguientes:

    • Se calcula un nuevo número de días desde la época, equivalente a dividir la magnitud de y por el número de tics de 100 nanosegundos en un período de 24 horas, se trunca la parte decimal del resultado y se suma este valor a los días de x desde la época.

    • Se calcula un nuevo tic desde la medianoche equivalente a sumar la magnitud de y a los tics de x desde la medianoche, módulo el número de tics de 100 nanosegundos en un período de 24 horas. Si x no especifica un valor para los tics desde la medianoche, se supone un valor de 0.

    • Se copia el valor de x para el desplazamiento de minutos con respecto a la hora UTC sin modificar.

  • Si no se especifican los días de la fecha y hora desde el valor de época, se construye un nuevo objeto datetime con los elementos de información siguientes:

    • Se calcula un nuevo tic desde la medianoche equivalente a sumar la magnitud de y a los tics de x desde la medianoche, módulo el número de tics de 100 nanosegundos en un período de 24 horas. Si x no especifica un valor para los tics desde la medianoche, se supone un valor de 0.

    • Se copian los valores de x de los días desde la época y el desplazamiento de minutos con respecto a la hora UTC sin modificar.

En los ejemplos siguientes se muestra cómo calcular la suma temporal absoluta cuando la fecha y hora especifica los días desde la época:

#date(2010,05,20) + #duration(0,8,0,0) 
    //#datetime( 2010, 5, 20, 8, 0, 0 ) 
    //2010-05-20T08:00:00 
 
#date(2010,01,31) + #duration(30,08,0,0) 
    //#datetime(2010, 3, 2, 8, 0, 0) 
    //2010-03-02T08:00:00 
 
#datetime(2010,05,20,12,00,00,-08) + #duration(0,04,30,00) 
    //#datetime(2010, 5, 20, 16, 30, 0, -8, 0) 
    //2010-05-20T16:30:00-08:00 
 
#datetime(2010,10,10,0,0,0,0) + #duration(1,0,0,0) 
   //#datetime(2010, 10, 11, 0, 0, 0, 0, 0) 
   //2010-10-11T00:00:00+00:00

En el ejemplo siguiente se muestra cómo calcular el desplazamiento de fecha y hora por duración para una hora concreta:

#time(8,0,0) + #duration(30,5,0,0) 
   //#time(13, 0, 0) 
   //13:00:00

Operador de resta

La interpretación del operador de resta (x - y) depende del tipo de valor de las expresiones x e y evaluadas, como se indica a continuación:

x Y Resultado Interpretación
type number type number type number Diferencia numérica
type number null null
null type number null
type duration type duration type duration Diferencia numérica de magnitudes
type duration null null
null type duration null
typedatetime typedatetime type duration Duración entre fechas y horas
typedatetime type duration typedatetime Desplazamiento de fecha y hora según la duración negada
typedatetime null null
null typedatetime null

En la tabla, typedatetime representa type date, type datetime, type datetimezone o type time. Al restar una duración de un valor de algún tipo datetime, el valor resultante es del mismo tipo.

Para otras combinaciones de valores que no sean las enumeradas en la tabla, se genera un error con el código de motivo "Expression.Error". Cada combinación se describe en las secciones siguientes.

Se propagan los errores que se producen al evaluar cualquiera de los operandos.

Diferencia numérica

La diferencia entre dos números se calcula mediante el operador de resta, lo que genera un número. Por ejemplo:

1 - 1                // 0 
#nan - #infinity     // #nan

El operador de resta - en números usa la precisión doble; la función de la biblioteca estándar Value.Subtract se puede usar para especificar la precisión decimal. Al calcular una diferencia de números sucede lo siguiente:

  • La diferencia en precisión doble se calcula de acuerdo a las reglas aritméticas de IEEE 754 de precisión doble binaria de 64 bits IEEE 754-2008. En la tabla siguiente se enumeran los resultados de todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y NaN. En la tabla, x e y son valores finitos distintos de cero y z es el resultado de x - y. Si x e y son iguales, z es cero positivo. Si x - y es demasiado grande para representarse en el tipo de destino, z es un infinito con el mismo signo que x - y.

    - y +0 -0 +∞ -∞ NaN
    x z x x -∞ +∞ NaN
    +0 -y +0 +0 -∞ +∞ NaN
    -0 -y -0 +0 -∞ +∞ NaN
    +∞ +∞ +∞ +∞ NaN +∞ NaN
    -∞ -∞ -∞ -∞ -∞ NaN NaN
    NaN NaN NaN NaN NaN NaN NaN
  • La diferencia en precisión decimal se calcula sin perder precisión. La escala del resultado es la mayor de las escalas de los dos operandos.

Resta de las duraciones

La diferencia de dos duraciones es la duración que representa la diferencia entre el número de tics de 100 nanosegundos representados por cada duración. Por ejemplo:

#duration(1,2,30,0) - #duration(0,0,0,30.45) 
// #duration(1, 2, 29, 29.55)

Desplazamiento de fecha y hora según la duración negada

Se puede restar un valor datetimex y una duración y con x - y para calcular un nuevo valor datetime. Aquí, datetime representa date, datetime, datetimezone o time. El valor datetime resultante tiene una distancia con respecto a x en una escala de tiempo lineal que es exactamente la magnitud de y, en la dirección opuesta al signo de y. La resta de duraciones positivas genera resultados anteriores en el tiempo con respecto a x, mientras que la resta de valores negativos genera resultados posteriores en el tiempo.

#date(2010,05,20) - #duration(00,08,00,00) 
   //#datetime(2010, 5, 19, 16, 0, 0) 
   //2010-05-19T16:00:00 
#date(2010,01,31) - #duration( 30,08,00,00) 
   //#datetime(2009, 12, 31, 16, 0, 0) 
   //2009-12-31T16:00:00

Duración entre valores datetime

Se pueden restar dos valores datetimet y u con t - u para calcular la duración entre ellos. Aquí, datetime representa date, datetime, datetimezone o time. La duración que se genera al restar u de t debe producir t cuando se suma a u.

#date(2010,01,31) - #date(2010,01,15) 
// #duration(16,00,00,00) 
// 16.00:00:00 
 
#date(2010,01,15)- #date(2010,01,31) 
// #duration(-16,00,00,00) 
// -16.00:00:00 
 
#datetime(2010,05,20,16,06,00,-08,00) - 
#datetime(2008,12,15,04,19,19,03,00) 
// #duration(521,22,46,41)
// 521.22:46:41

Al restar t - u cuando u > t se genera una duración negativa:

#time(01,30,00) - #time(08,00,00) 
// #duration(0, -6, -30, 0)

Cuando se restan dos valores datetime mediante t - u, sucede lo siguiente:

  • u + (t - u) = t

Operador de multiplicación

La interpretación del operador de multiplicación (x * y) depende del tipo de valor de las expresiones x e y evaluadas, como se indica a continuación:

X Y Resultado Interpretación
type number type number type number Producto numérico
type number null null
null type number null
type duration type number type duration Múltiplo de duración
type number type duration type duration Múltiplo de duración
type duration null null
null type duration null

Para otras combinaciones de valores que no sean las enumeradas en la tabla, se genera un error con el código de motivo "Expression.Error". Cada combinación se describe en las secciones siguientes.

Se propagan los errores que se producen al evaluar cualquiera de los operandos.

Producto numérico

El producto de dos números se calcula mediante el operador de multiplicación, lo que genera un número. Por ejemplo:

2 * 4                // 8 
6 * null             // null 
#nan * #infinity     // #nan

El operador de multiplicación * en números usa la precisión doble; la función de la biblioteca estándar Value.Multiply se puede usar para especificar la precisión decimal. Al calcular un producto de números sucede lo siguiente:

  • El producto en precisión doble se calcula de acuerdo a las reglas aritméticas de IEEE 754 de precisión doble binaria de 64 bits IEEE 754-2008. En la tabla siguiente se enumeran los resultados de todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y NaN. En la tabla, x e y son valores finitos positivos. z es el resultado de x * y. Si el resultado es demasiado grande para el tipo de destino, z es infinito. Si el resultado es demasiado pequeño para el tipo de destino, z es cero.

    * +y -y +0 -0 +∞ -∞ NaN
    +x +z -Z +0 -0 +∞ -∞ NaN
    -x -Z +z -0 +0 -∞ +∞ NaN
    +0 +0 -0 +0 -0 NaN NaN NaN
    -0 -0 +0 -0 +0 NaN NaN NaN
    +∞ +∞ -∞ NaN NaN +∞ -∞ NaN
    -∞ -∞ +∞ NaN NaN -∞ +∞ NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • El producto en precisión decimal se calcula sin perder precisión. La escala del resultado es la mayor de las escalas de los dos operandos.

Múltiplos de duraciones

El producto de una duración y un número es la duración que representa el número de tics de 100 nanosegundos representados por el operando de duración multiplicado por el de número. Por ejemplo:

#duration(2,1,0,15.1) * 2 
// #duration(4, 2, 0, 30.2)

Operador de división

La interpretación del operador de división (x / y) depende del tipo de valor de las expresiones x e y evaluadas, como se indica a continuación:

X Y Resultado Interpretación
type number type number type number Cociente numérico
type number null null
null type number null
type duration type number type duration Fracción de duración
type duration type duration type duration Cociente numérico de duraciones
type duration null null
null type duration null

Para otras combinaciones de valores que no sean las enumeradas en la tabla, se genera un error con el código de motivo "Expression.Error". Cada combinación se describe en las secciones siguientes.

Se propagan los errores que se producen al evaluar cualquiera de los operandos.

Cociente numérico

El cociente de dos números se calcula mediante el operador de división, lo que genera un número. Por ejemplo:

8 / 2               // 4 
8 / 0               // #infinity 
0 / 0               // #nan 
0 / null            // null 
#nan / #infinity    // #nan

El operador de división / en números usa la precisión doble; la función de la biblioteca estándar Value.Divide se puede usar para especificar la precisión decimal. Al calcular un cociente de números sucede lo siguiente:

  • El cociente en precisión doble se calcula de acuerdo a las reglas aritméticas de IEEE 754 de precisión doble binaria de 64 bits IEEE 754-2008. En la tabla siguiente se enumeran los resultados de todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y NaN. En la tabla, x e y son valores finitos positivos. z es el resultado de x / y. Si el resultado es demasiado grande para el tipo de destino, z es infinito. Si el resultado es demasiado pequeño para el tipo de destino, z es cero.

    / +y -y +0 -0 +∞ -∞ NaN
    +x +z -Z +∞ -∞ +0 -0 NaN
    -x -Z +z -∞ +∞ -0 +0 NaN
    +0 +0 -0 NaN NaN +0 -0 NaN
    -0 -0 +0 NaN NaN -0 +0 NaN
    +∞ +∞ -∞ +∞ -∞ NaN NaN NaN
    -∞ -∞ +∞ -∞ +∞ NaN NaN NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • La suma en precisión decimal se calcula sin perder precisión. La escala del resultado es la mayor de las escalas de los dos operandos.

Cociente de duraciones

El cociente de dos duraciones es el número que representa el cociente del número de tics de 100 nanosegundos representados por las duraciones. Por ejemplo:

#duration(2,0,0,0) / #duration(0,1,30,0) 
// 32

Duraciones escaladas

El cociente de una duración x y un número y es la duración que representa el cociente del número de tics de 100 nanosegundos representados por la duración x y el número y. Por ejemplo:

#duration(2,0,0,0) / 32 
// #duration(0,1,30,0)

Combinación de estructura

El operador de combinación (x & y) se define en los tipos de valores siguientes:

X Y Resultado Interpretación
type text type text type text Concatenación
type text null null
null type text null
type date type time type datetime Combinar
type date null null
null type time null
type list type list type list Concatenación
type record type record type record Combinar
type table type table type table Concatenación

Concatenación

Dos valores de texto, dos listas o dos tablas se pueden concatenar mediante x & y.

En el ejemplo siguiente se muestra la concatenación de valores de texto:

"AB" & "CDE"     // "ABCDE"

En el ejemplo siguiente se muestra la concatenación de listas:

{1, 2} & {3}     // {1, 2, 3}

Cuando se concatenan dos valores mediante x & y, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones x o y.

  • No se propaga ningún error si un elemento de x o y contiene un error.

  • El resultado de concatenar dos valores de texto es un valor de texto que contiene el valor de x inmediatamente seguido de y. Si alguno de los operandos es NULL y el otro es un valor de texto, el resultado es NULL.

  • El resultado de la concatenación de dos listas es una lista que contiene todos los elementos de x seguidos de todos los elementos de y.

  • El resultado de la concatenación de dos tablas es una tabla que tiene la unión de las columnas de las dos tablas de operandos. Se conserva el orden de las columnas de x, seguido de las columnas que solo aparecen en y, para conservar su orden relativo. En el caso de las columnas que solo aparecen en uno de los operandos, se usa null para rellenar los valores de celda para el otro operando.

Combinar

Combinación de registros

Dos registros se pueden combinar mediante x & y, lo que genera un registro que incluye los campos de x e y.

En los ejemplos siguientes se muestra la combinación de registros:

[ x = 1 ] & [ y = 2 ]                // [ x = 1, y = 2 ] 
[ x = 1, y = 2 ] & [ x = 3, z = 4 ]  // [ x = 3, y = 2, z = 4 ]

Cuando se combinan dos registros mediante x + y, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones x o y.

  • Si un campo aparece en x e y, se usa el valor de y.

  • El orden de los campos en el registro resultante es el de x, seguido de los campos de y que no forman parte de x, en el mismo orden en que aparecen en y.

  • La combinación de registros no provoca la evaluación de los valores.

  • No se produce ningún error porque un campo contenga un error.

  • El resultado es un registro.

Combinación de fecha y hora

Una fecha x se puede combinar con una hora y mediante x & y, lo que genera un valor de fecha y hora que combina las partes de x e y.

En el ejemplo siguiente se muestra cómo combinar una fecha y una hora:

#date(2013,02,26) & #time(09,17,00) 
// #datetime(2013,02,26,09,17,00)

Cuando se combinan dos registros mediante x + y, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar las expresiones x o y.

  • El resultado es un valor de fecha y hora.

Operadores unarios

Los operadores +, - y not son operadores unarios.

unary-expression:
      type-expression

      +unary-expression
      -unary-expression
      notunary-expression

Operador unario más

El operador unario más (+x) se define para los tipos de valores siguientes:

X Resultado Interpretación
type number type number Suma unaria
type duration type duration Suma unaria
null `null

Para otros valores, se genera un error con el código de motivo "Expression.Error".

El operador unario más permite aplicar un + signo a un valor de número, fecha y hora, o NULL. El resultado es ese mismo valor. Por ejemplo:

+ - 1                 // -1 
+ + 1                 // 1 
+ #nan                // #nan 
+ #duration(0,1,30,0) // #duration(0,1,30,0)

Al evaluar el operador unario más +x, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar x.

  • Si el resultado de evaluar x no es un valor numérico, se genera un error con el código de motivo "Expression.Error".

Operador unario menos

El operador unario menos (-x) se define para los tipos de valores siguientes:

X Resultado Interpretación
type number type number Negación
type duration type duration Negación
null null

Para otros valores, se genera un error con el código de motivo "Expression.Error".

El operador unario menos se usa para cambiar el signo de un número o de una duración. Por ejemplo:

- (1 + 1)       // -2 
- - 1           // 1 
- - - 1         // -1 
- #nan          // #nan 
- #infinity     // -#infinity 
- #duration(1,0,0,0)  // #duration(-1,0,0,0) 
- #duration(0,1,30,0) // #duration(0,-1,-30,0)

Al evaluar el operador unario menos -x, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar x.

  • Si la expresión es un número, el resultado es el valor numérico de la expresión x con el signo cambiado. Si el valor es NaN, el resultado también es NaN.

Operador de negación lógica.

El operador de negación lógica (not) se define para los tipos de valores siguientes:

X Resultado Interpretación
type logical type logical Negación
null null

Este operador calcula la operación not lógica en un valor lógico determinado. Por ejemplo:

not true             // false 
not false            // true 
not (true and true)  // false

Al evaluar el operador de negación lógica not x, sucede lo siguiente:

  • Se propagan los errores que se producen al evaluar x.

  • El valor generado de la evaluación de la expresión x debe ser un valor lógico, o bien se debe generar un error con el código de motivo "Expression.Error". Si el valor es true, el resultado es false. Si el operando es false, el resultado es true.

El resultado es un valor lógico.

Operadores de tipo

Los operadores is y as se conocen como operadores de tipo.

Operador de compatibilidad de tipos

El operador de compatibilidad de tipos x is y se define para los tipos de valores siguientes:

X Y Resultado
type any nullable-primitive-type type logical

La expresión x is y devuelve true si el tipo asignado de x es compatible con y y devuelve false si el tipo asignado de x es incompatible con y. y debe ser nullable-primitivetype.

is-expression:
      as-expression
      is-expression
isnullable-primitive-type
nullable-primitive-type:

      nullableopt primitive-type

La compatibilidad de tipos, tal como la admite el operador is, es un subconjunto de la compatibilidad con tipos generales y se define mediante las reglas siguientes:

  • Si x es NULL, es compatible si y es el tipo any, el tipo null o el tipo que acepta valores NULL.

  • Si x no es NULL, es compatible si el tipo primitivo de x es igual que y.

Cuando se evalúa la expresión x is y sucede lo siguiente:

  • Se propaga un error que se produce al evaluar la expresión x.

Operador de aserción de tipos

El operador de aserción de tipos x as y se define para los tipos de valores siguientes:

X Y Resultado
type any nullable-primitive-type type any

La expresión x as y afirma que el valor x es compatible con y según el operador is. Si no es compatible, se produce un error. y debe ser nullable-primitive-type.

as-expression:
      equality-expression
      as-expression
asnullable-primitive-type

La expresión x as y se evalúa de esta forma:

  • Se realiza una comprobación de compatibilidad de tipos x is y y la aserción devuelve x sin modificar si la prueba se realiza correctamente.

  • Si se produce un error en la comprobación de compatibilidad, se genera un error con el código de motivo "Expression.Error".

Ejemplos:

1 as number               // 1 
"A" as number             // error 
null as nullable number   // null

Cuando se evalúa la expresión x as y sucede lo siguiente:

  • Se propaga un error que se produce al evaluar la expresión x.

Operador de fusión

El operador de fusión ?? devuelve el resultado de su operando izquierdo si no es NULL; de lo contrario, devolverá el resultado de su operando derecho. El operando derecho solamente se evalúa si el operando izquierdo no es NULL.