Share via


Funciones (F#)

Las funciones representan la unidad fundamental de la ejecución de un programa en cualquier lenguaje de programación. Al igual que en otros lenguajes, las funciones en F# tienen un nombre, pueden tener parámetros y recibir argumentos, y tienen un cuerpo. F# también admite construcciones de programación funcional, como el tratamiento de las funciones como valores, el uso de funciones sin nombre en expresiones, la composición de funciones para formar nuevas funciones, funciones currificadas y la definición implícita de funciones mediante la aplicación parcial de argumentos de función.

Las funciones se definen mediante la palabra clave let o, si la función es recursiva, mediante la combinación de palabras clave let rec.

// Non-recursive function definition.
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body

Comentarios

function-name es un identificador que representa la función. parameter-list se compone de parámetros sucesivos separados con un espacio. Se puede especificar un tipo explícito para cada parámetro, tal y como se describe en la sección Parámetros. Si no se especifica un tipo concreto para un argumento, el compilador intenta deducir el tipo a partir del cuerpo de la función. function-body se compone de una expresión. La expresión que compone el cuerpo de la función suele ser una expresión compuesta, formada por varias expresiones que culminan en una expresión final que es el valor devuelto. return-type es un signo de dos puntos seguido de un tipo y es opcional. Si no se especifica explícitamente el tipo del valor devuelto, el compilador lo determinará a partir de la expresión final.

Una definición de función simple se parecerá a la siguiente:

let f x = x + 1

En el ejemplo anterior, el nombre de función es f, el argumento es x, cuyo tipo es int, el cuerpo de la función es x + 1 y el tipo del valor devuelto es int.

El especificador inline indica al compilador que la función es pequeña y que su código se puede integrar en el cuerpo de la función que la llame.

Ámbito

En cualquier nivel de ámbito que no sea el ámbito de módulo, no es un error reutilizar un valor o nombre de función. Si se reutiliza un nombre, el último nombre declarado prevalece sobre el declarado anteriormente. Sin embargo, en el ámbito de nivel superior de un módulo, los nombres deben ser únicos. Por ejemplo, el código siguiente genera un error cuando aparece en el ámbito de módulo, pero no genera ningún error cuando aparece en una función:

let list1 = [ 1; 2; 3]
// Error: duplicate definition.
let list1 = []  
let function1 =
   let list1 = [1; 2; 3]
   let list1 = []
   list1

Sin embargo, el código siguiente es aceptable en cualquier nivel de ámbito:

let list1 = [ 1; 2; 3]
let sumPlus x =
// OK: inner list1 hides the outer list1.
   let list1 = [1; 5; 10]  
   x + List.sum list1

Parámetros

Los nombres de los parámetros aparecen después del nombre de la función. Se puede especificar un tipo para un parámetro, tal y como se muestra en el ejemplo siguiente:

let f (x : int) = x + 1

Si se especifica un tipo, debe figurar después del nombre del parámetro e ir separado del nombre mediante un signo de dos puntos. Si se omite el tipo del parámetro, el compilador lo inferirá. Por ejemplo, en la definición de función siguiente, se infiere que el tipo del argumento x es int porque 1 es de tipo int.

let f x = x + 1

Sin embargo, el compilador intentará que la función sea lo más genérica posible. Por ejemplo, tenga en cuenta el código siguiente:

let f x = (x, x)

La función crea una tupla a partir de un argumento de cualquier tipo. Dado que no se especifica el tipo, la función puede usarse con cualquier tipo de argumento. Para obtener más información, vea Generalización automática (F#).

Para obtener más información sobre parámetros, vea Parámetros y argumentos (F#).

Cuerpos de función

El cuerpo de una función puede contener definiciones de variables y funciones locales. Estas variables y funciones existen en el ámbito del cuerpo de la función actual, pero no fuera del mismo. Cuando está habilitada la opción de sintaxis simplificada, se debe utilizar la sangría para indicar que una definición pertenece al cuerpo de una función, tal y como se muestra en el ejemplo siguiente:

let cylinderVolume radius length =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

Para obtener más información, vea Instrucciones de formato de código (F#) y Sintaxis detallada (F#).

Valores devueltos

El compilador utiliza la expresión final del cuerpo de una función para determinar el valor devuelto y el tipo de dicho valor. El compilador puede inferir el tipo de la expresión final a partir de expresiones anteriores. En la función cylinderVolume, que se muestra en la sección anterior, se determina que el tipo de pi es float a partir del tipo del literal 3.14159. El compilador usa el tipo de pi para determinar que el tipo de la expresión h * pi * r * r es float. Por consiguiente, el tipo global del valor devuelto por la función es float.

Para especificar explícitamente el valor devuelto, escriba el código de la siguiente manera:


let cylinderVolume radius length : float =
   // Define a local value pi.
   let pi = 3.14159
   length * pi * radius * radius

De esta manera, el compilador aplica float a toda la función; si desea aplicarlo también a los tipos de parámetro, use el código siguiente:

let cylinderVolume (radius : float) (length : float) : float

Una función siempre devuelve exactamente un valor. Si una función no devuelve un valor real, el valor de unit se puede devolver. Puede devolver varios fragmentos de datos de distintas maneras. Una de las formas es devolver un valor de tupla. Una función que devuelve un valor de tupla, puede utilizar un modelo de tupla en un enlace let para asignar los elementos de tupla a más de un valor. Esto se ilustra en el código siguiente:

let firstAndLast list1 =
    (List.head list1, List.head (List.rev list1))

let (first, last) = firstAndLast [ 1; 2; 3 ]
printfn "First: %d; Last: %d" first last

La salida del código anterior es la siguiente:

  

Otra forma de devolver varios fragmentos de datos es utilizar celdas de referencia, y una tercera forma es usar parámetros byref. Para obtener más información y ejemplos, vea Celdas de referencia (F#) y Parámetros y argumentos (F#).

Llamar a una función

Para llamar a una función, se especifica el nombre de la función seguido de un espacio y, a continuación, se especifican los argumentos separados por un espacio. Por ejemplo, para llamar a la función cylinderVolume y asignar el resultado al valor vol, escriba el código siguiente:

let vol = cylinderVolume 2.0 3.0

Si una función toma una tupla como único parámetro, finaliza una llamada de función con una lista entre paréntesis que es similar a una lista de argumentos en otros lenguajes. El espacio entre el nombre de función y el paréntesis de apertura se puede omitir. Por ejemplo, a continuación se muestra la función cylinderVolume con una tupla como parámetro.

let cylinderVolume(radius, length) =
    let pi = 3.14159
    length * pi * radius * radius

Con una tupla como argumento, la llamada a la función tiene el siguiente aspecto.

let vol = cylinderVolume(2.0, 3.0)

Si una función no toma ningún parámetro, especifique el valor de unit () como argumento, como en la siguiente línea de código.

initializeApp()

El nombre de una función en sí mismo es simplemente un valor de función, por lo que, si se omiten los paréntesis que indican el valor de unit, no se llamará a la función, simplemente se hace referencia a esta.

Aplicación parcial de argumentos

Si se proporciona un número de argumentos menor que el especificado, se crea una nueva función que espera los argumentos restantes. Este método de control de argumentos se denomina currificación y es una característica de los lenguajes de programación funcional como F#. Por ejemplo, supongamos que usa dos tamaños de canalización: uno tiene un radio de 2.0 y el otro tiene un radio de 3.0. Podrá crear funciones que determinen el volumen de canalización de la siguiente manera:

let smallPipeRadius = 2.0
let bigPipeRadius = 3.0

// These define functions that take the length as a remaining
// argument:

let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius

A continuación, proporcionará el argumento adicional según sea necesario para diversas longitudes de canalización de los dos tamaños:

let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2

Funciones recursivas

Las funciones recursivas son funciones que se llaman a sí mismas. Requieren que se especifique la palabra clave rec después de la palabra clave let. La función recursiva debe invocarse desde el cuerpo de la función de la misma manera que se invoca cualquier llamada de función. La siguiente función recursiva calcula el enésimo número de Fibonacci. La secuencia de números de Fibonacci se conoce desde la antigüedad. Es una secuencia en la que cada número sucesivo es la suma de los dos números anteriores de la secuencia.

let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)

Algunas funciones recursivas podrían desbordar la pila de programas o ser ineficaces si no se escriben con cuidado y con conocimiento de determinadas técnicas especiales, como el uso de acumuladores y continuaciones.

Valores de función

En F#, todas las funciones se consideran valores; de hecho, se conocen como valores de función. Dado que las funciones son valores, se pueden utilizar como argumentos de otras funciones o en otros contextos donde se usen valores. A continuación figura un ejemplo de una función que recibe un valor de función como argumento:

let apply1 (transform : int -> int ) y = transform y

El tipo de un valor de función se especifica mediante el token ->. A la izquierda de este token figura el tipo del argumento y a la derecha aparece el tipo del valor devuelto. En el ejemplo anterior, apply1 es una función que recibe una función transform como argumento, donde transform es una función que recibe un entero y devuelve otro entero. En el siguiente código se muestra cómo usar apply1:

let increment x = x + 1

let result1 = apply1 increment 100

El valor de result será 101 después de que se ejecute el código anterior.

Si la función recibe varios argumentos, estos van separados por sucesivos tokens ->, tal y como se muestra en el siguiente ejemplo:

let apply2 ( f: int -> int -> int) x y = f x y

let mul x y = x * y

let result2 = apply2 mul 10 20

El resultado es 200.

Expresiones lambda

Una expresión lambda es una función sin nombre. En los ejemplos anteriores, en lugar de definir las funciones con nombre increment y mul, podría usar expresiones lambda de la siguiente manera:

let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y ) 10 20

Las expresiones lambda se definen mediante la palabra clave fun. Una expresión lambda es similar a una definición de función, con la diferencia de que se usa el token -> en lugar de = para separar la lista de argumentos del cuerpo de la función. Al igual que en una definición de función normal, los tipos de los argumentos se pueden inferir o especificar explícitamente, y el tipo del valor devuelto por la expresión lambda se deduce a partir del tipo de la última expresión del cuerpo. Para obtener más información, vea Expresiones lambda: la palabra clave fun (F#).

Composición de funciones y canalización

En F# se pueden crear funciones mediante la composición de otras funciones. La composición de las funciones function1 y function2 produce otra función que representa la aplicación de function1 seguida de la aplicación de function2:

let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100

El resultado es 202.

La canalización permite encadenar las llamadas de función como operaciones sucesivas. La canalización funciona de la siguiente manera:

let result = 100 |> function1 |> function2

Una vez más, el resultado es 202.

Vea también

Otros recursos

Valores (F#)

Referencia del lenguaje F#

Historial de cambios

Fecha

Historial

Motivo

Mayo de 2010

Se corrigió el ejemplo de código en la sección de parámetros.

Corrección de errores de contenido.

1 de abril de 2011

Se agregó información de clarificación sobre los parámetros y valores devueltos.