Share via


Funciones

Una función es un valor que representa una asignación de un conjunto de valores de argumento a un valor único. Una función se invoca mediante un conjunto de valores de entrada (los valores de argumento) y genera un único valor de salida (el valor devuelto).

Escritura de funciones

Las funciones se escriben mediante function-expression (expresión de función):

function-expression:
      (parameter-listopt)function-return-typeopt=>function-body
function-body:
      expresión
parameter-list:
      fixed-parameter-list
      fixed-parameter-list
,optional-parameter-list
      optional-parameter-list
fixed-parameter-list:
      parámetro
      parameter
,fixed-parameter-list
:
      parameter-name parameter-typeopt
parameter-name:
      identificador
parameter-type:
      assertion
function-return-type:
      assertion
assertion:

      asnullable-primiitve-type
optional-parameter-list:
      optional-parameter
      optional-parameter
,optional-parameter-list
optional-parameter:

      optionalparameter
nullable-primitve-type
      nullable
optprimitive-type

En el ejemplo siguiente se muestra una función que requiere exactamente dos valores x e y, y genera el resultado de aplicar el operador + a esos valores. x y y son parámetros que forman parte del elemento parameter-list de la función, y x + y es function-body:

(x, y) => x + y

El resultado de evaluar una expresión de función es para generar un valor de función (no para evaluar el cuerpo de la función). Como convención en este documento, los valores de función (en contraposición a las expresiones de función) se muestran con formal-parameter-list pero con puntos suspensivos (...) en lugar de function-body. Por ejemplo, una vez que se ha evaluado la expresión de función anterior, se mostraría como el siguiente valor de función:

 (x, y) => ...

Los operadores siguientes se definen para los valores de función:

Operador Resultado
x = y Igual
x <> y No igual a

El tipo nativo de los valores de función es un tipo de función personalizado (derivado del tipo intrínseco function) que enumera los nombres de parámetro y especifica que todos los tipos de parámetro y el tipo de valor devuelto sean any. (Visite Tipos de función para saber más sobre los tipos de funciones).

Invocación de funciones

El elemento function-body de una función se ejecuta al invocar el valor de la función mediante invoke-expression. La invocación de un valor de función significa que el cuerpo de la función del valor de función se evalúa y se devuelve un valor, o bien se produce un error.

invoke-expression:
      primary-expression
(argument-listopt)
argument-list:
      expression-list

Cada vez que se invoca un valor de función, se especifica un conjunto de valores como argument-list (lista de argumentos), denominados argumentos de la función.

argument-list se usa para especificar un número fijo de argumentos directamente como una lista de expresiones. En el ejemplo siguiente se define un registro con un valor de función en un campo y, después, se invoca la función desde otro campo del registro:

[ 
    MyFunction = (x, y, z) => x + y + z, 
    Result1 = MyFunction(1, 2, 3)           // 6
]

Al invocar una función, sucede lo siguiente:

  • El entorno que se usa para evaluar el cuerpo de la función de la función incluye una variable que se corresponde a cada parámetro, con el mismo nombre que el parámetro. El valor de cada parámetro se corresponde a un valor construido a partir de argument-list de invoke-expression, como se define en Parámetros.

  • Todas las expresiones correspondientes a los argumentos de la función se evalúan antes que el cuerpo de la función.

  • Se propagan los errores que se producen al evaluar las expresiones de expression-list o function-body.

  • El número de argumentos construido a partir de argument-list debe ser compatible con los parámetros de la función, o bien se produce un error con el código de motivo "Expression.Error". El proceso para determinar la compatibilidad se define en Parámetros.

Parámetros

parameter-list puede contener dos tipos de parámetros:

  • Un parámetro obligatorio indica que cuando se invoca una función siempre se debe especificar un argumento correspondiente al parámetro. Los parámetros obligatorios se deben especificar en primer lugar en parameter-list. En el ejemplo siguiente, la función define los parámetros obligatorios x e y:

      [ 
          MyFunction = (x, y) => x + y, 
    
          Result1 = MyFunction(1, 1),     // 2 
          Result2 = MyFunction(2, 2)      // 4
      ] 
    
  • Un parámetro opcional indica que, cuando se invoca una función, se puede especificar un argumento correspondiente al parámetro, pero no es obligatorio hacerlo. Si no se especifica un argumento que se corresponda con un parámetro opcional al invocar la función, en su lugar se usa el valor null. Los parámetros opcionales deben aparecer después de los parámetros obligatorios en parameter-list. La función del ejemplo siguiente define un parámetro fijo x y un parámetro opcional y:

      [ 
          MyFunction = (x, optional y) =>
                            if (y = null) x else x + y, 
          Result1 = MyFunction(1),        // 1 
          Result2 = MyFunction(1, null),  // 1 
          Result3 = MyFunction(2, 2),     // 4
      ] 
    

El número de argumentos que se especifican al invocar una función debe ser compatible con la lista de parámetros. La compatibilidad de un conjunto de argumentos A para una función F se calcula de esta forma:

  • Se permite que el valor N represente el número de argumentos A construidos a partir de argument-list. Por ejemplo:

      MyFunction()             // N = 0 
      MyFunction(1)            // N = 1 
      MyFunction(null)         // N = 1 
      MyFunction(null, 2)      // N = 2 
      MyFunction(1, 2, 3)      // N = 3 
      MyFunction(1, 2, null)   // N = 3 
      MyFunction(1, 2, {3, 4}) // N = 3
    
  • Se permita que el valor Required represente el número de parámetros fijos de F y Optional el número de parámetros opcionales de F. Por ejemplo:

    ()               // Required = 0, Optional = 0 
    (x)              // Required = 1, Optional = 0 
    (optional x)     // Required = 0, Optional = 1 
    (x, optional y)  // Required = 1, Optional = 1
    
  • Los argumentos A son compatibles con la función F si se cumple lo siguiente:

    • (N >= Fijo) y (N <= (Fijo + Opcional))
    • Los tipos de argumento son compatibles con los tipos de parámetro correspondientes de F
  • Si la función tiene un tipo de valor devuelto declarado, el valor del resultado del cuerpo de la función F es compatible con el tipo de valor devuelto de F si se cumple lo siguiente:

    • El valor generado al evaluar el cuerpo de la función con los argumentos proporcionados para los parámetros de la función tiene un tipo que es compatible con el tipo de valor devuelto.
  • Si el cuerpo de la función genera un valor incompatible con el tipo de valor devuelto de la función, se genera un error con el código de motivo "Expression.Error".

Funciones recursivas

Para escribir un valor de función que sea recursivo, es necesario usar el operador de ámbito (@) para hacer referencia a la función dentro de su ámbito. Por ejemplo, el registro siguiente contiene un campo que define la función Factorial y otro campo que la invoca:

[ 
    Factorial = (x) => 
                if x = 0 then 1 else x * @Factorial(x - 1), 
    Result = Factorial(3)  // 6 
]

De forma similar, se pueden escribir funciones mutuamente recursivas siempre que cada función a la que se deba acceder tenga un nombre. En el ejemplo siguiente, parte de la función Factorial se ha refactorizado en una segunda función Factorial2.

[ 
    Factorial = (x) => if x = 0 then 1 else Factorial2(x), 
    Factorial2 = (x) => x * Factorial(x - 1), 
    Result = Factorial(3)     // 6 
]

Cierres

Una función puede devolver otra función como un valor. A su vez, esta función puede depender de uno o más parámetros de la función original. En el ejemplo siguiente, la función asociada al campo MyFunction devuelve una función que devuelve el parámetro especificado para ella:

[ 
    MyFunction = (x) => () => x, 
    MyFunction1 = MyFunction(1), 
    MyFunction2 = MyFunction(2), 
    Result = MyFunction1() + MyFunction2()  // 3 
]

Cada vez que se invoca la función, se devolverá un nuevo valor de función que mantiene el valor del parámetro de modo que, cuando se invoca, se devolverá el valor del parámetro.

Funciones y entornos

Además de parámetros, el cuerpo de la función de una expresión de función puede hacer referencia a las variables que se encuentran en el entorno cuando se inicializa la función. Por ejemplo, la función definida por el campo MyFunction accede al campo C del registro contenedor A:

[ 
A =  
    [ 
        MyFunction = () => C, 
        C = 1 
    ], 
B = A[MyFunction]()           // 1 
]

Cuando se invoca MyFunction, accede al valor de la variable C, aunque se invoque desde un entorno (B) que no contenga una variable C.

Declaraciones simplificadas

each-expression es una abreviatura sintáctica para declarar funciones sin tipo que toman un único parámetro denominado _ (carácter de subrayado).

each-expression:
      eacheach-expression-body
each-expression-body:
      function-body

Las declaraciones simplificadas se usan normalmente para mejorar la legibilidad de la invocación de funciones de orden superior.

Por ejemplo, los siguientes pares de declaraciones son semánticamente equivalentes:

each _ + 1 
(_) => _ + 1  
each [A] 
(_) => _[A] 
 
Table.SelectRows( aTable, each [Weight] > 12 ) 
Table.SelectRows( aTable, (_) => _[Weight] > 12 )