Compartir a través de


Parámetros y argumentos (F#)

Este tema describe la compatibilidad con lenguajes para definir parámetros y pasar argumentos a las funciones, los métodos y las propiedades.Incluye información sobre cómo pasar argumentos por referencia y cómo definir y utilizar métodos que pueden tomar un número variable de argumentos.

Parámetros y argumentos

El término parámetro se utiliza para describir los nombres de los valores que se espera que se proporcionen.El término argumento se utiliza para los valores proporcionados para cada parámetro.

Los parámetros se pueden especificar en forma de tupla o currificada, o en alguna combinación de ambas.Se pueden pasar argumentos utilizando un nombre de parámetro explícito.Los parámetros de los métodos se pueden especificar como opcionales y se les puede dar un valor predeterminado.

Modelos de parámetros

En general, los parámetros proporcionados a las funciones y los métodos son modelos separados por espacios.Esto significa que, en principio, cualquiera de los modelos descritos en Expresiones match (F#) se puede utilizar en una lista de parámetros para una función o un miembro.

Los métodos suelen utilizar la forma de tupla para pasar los argumentos.De este modo se consigue un resultado más claro desde la perspectiva de otros lenguajes .NET, porque la forma de tupla coincide con la manera en que se pasan los argumentos en los métodos de .NET.

La forma currificada se utiliza con mayor frecuencia con las funciones creadas mediante enlaces let.

En el siguiente pseudocódigo se muestran ejemplos de una tupla y de argumentos currificados.

// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...

Es posible combinar ambas formas cuando algunos argumentos están en tuplas y otros, no.

let function2 param1 (param2a, param2b) param3 = ...

También se pueden utilizar otros modelos en las listas de parámetros, pero si el modelo de parámetro no coincide con todas las posibles entradas, puede que en tiempo de ejecución se produzca una coincidencia incompleta.Se genera la excepción MatchFailureException cuando el valor de un argumento no coincide con los modelos especificados en la lista de parámetros.El compilador emite una advertencia cuando un modelo de parámetro permite coincidencias incompletas.Existe al menos otro modelo que suele resultar útil para las listas de parámetros, que es el modelo de carácter comodín.El modelo de carácter comodín se utiliza en una lista de parámetros cuando se desea omitir todos los argumentos que se proporcionen, sin más.En el código siguiente se muestra el uso del modelo de carácter comodín en una lista de argumentos.

let makeList _ = [ for i in 1 .. 100 -> i * i ]
// The arguments 100 and 200 are ignored.
let list1 = makeList 100
let list2 = makeList 200

El modelo de carácter comodín puede resultar útil siempre que no se necesite pasar argumentos, como en el punto de entrada principal a un programa, cuando no interesan los argumentos de línea de comandos que se suelen proporcionar en una matriz de cadena, como en el código siguiente.

[<EntryPoint>]
let main _ =
    printfn "Entry point!"
    0

Otros modelos que se utilizan a veces en argumentos son el modelo de as y los modelos de identificador asociados a las uniones discriminadas y los modelos activos.El modelo de unión discriminada de caso único se puede utilizar como sigue.

type Slice = Slice of int * int * string

let GetSubstring1 (Slice(p0, p1, text)) = 
    printfn "Data begins at %d and ends at %d in string %s" p0 p1 text
    text.[p0..p1]

let substring = GetSubstring1 (Slice(0, 4, "Et tu, Brute?"))
printfn "Substring: %s" substring

La salida es la siguiente.

Data begins at 0 and ends at 4 in string Et tu, Brute?
Et tu

Los modelos activos pueden ser útiles como parámetros, por ejemplo, al transformar un argumento en un formato deseado, como en el ejemplo siguiente:

type Point = { x : float; y : float }
let (| Polar |) { x = x; y = y} =
    ( sqrt (x*x + y*y), System.Math.Atan (y/ x) )

let radius (Polar(r, _)) = r
let angle (Polar(_, theta)) = theta

Se puede utilizar el modelo as para almacenar un valor coincidente como valor local, como se muestra en la línea de código siguiente.

let GetSubstring2 (Slice(p0, p1, text) as s) = s

Otro modelo que se utiliza de vez en cuando es una función que deja sin nombre el último argumento proporcionando, como cuerpo de la función, una expresión lambda que inmediatamente realiza una coincidencia de modelos con el argumento implícito.La línea de código siguiente es un ejemplo de ello.

let isNil = function [] -> true | _::_ -> false

Este código define una función que toma una lista genérica y devuelve true si la lista está vacía, y false, en caso contrario.El uso de tales técnicas puede hacer que el código resulte más difícil de leer.

En ocasiones, los modelos que conllevan coincidencias incompletas resultan útiles; por ejemplo, si se sabe con certeza que las listas del programa tienen únicamente tres elementos, se podría utilizar un modelo como el siguiente en una lista de parámetros.

let sum [a; b; c;] = a + b + c

Es preferible reservar el uso de modelos que tienen coincidencias incompletas para la creación rápida de signaturas y otros usos temporales.El compilador emitirá una advertencia en este tipo de código.Estos modelos no pueden cubrir el caso general de todas las posibles entradas y, por consiguiente, no son convenientes para las API de componentes.

Argumentos con nombre

Los argumentos de los métodos se pueden especificar por su posición en una lista de argumentos separados por comas, o se pueden pasar explícitamente a un método proporcionando el nombre, seguido de un signo de igualdad y del valor que se va a pasar.Si se especifican proporcionando el nombre, pueden aparecer en un orden diferente del que se usó en la declaración.

Los argumentos con nombre pueden hacer que el código resulte más legible y adaptable a algunos tipos de cambios en la API, como la reordenación de los parámetros de método.

Los argumentos con nombre se permiten únicamente para los métodos, no para las funciones enlazadas mediante let, los valores de función ni expresiones lambda.

En el siguiente código de ejemplo, se muestra el uso de los argumentos con nombre.

type SpeedingTicket() =
    member this.GetMPHOver(speed: int, limit: int) = speed - limit

let CalculateFine (ticket : SpeedingTicket) =
    let delta = ticket.GetMPHOver(limit = 55, speed = 70)
    if delta < 20 then 50.0 else 100.0

let ticket1 : SpeedingTicket = SpeedingTicket()
printfn "%f" (CalculateFine ticket1)

En una llamada a un constructor de clase, puede establecer los valores de las propiedades de la clase usando una sintaxis similar a la de argumentos con nombre.Esta sintaxis se muestra en el siguiente ejemplo.

 type Account() =
    let mutable balance = 0.0
    let mutable number = 0
    let mutable firstName = ""
    let mutable lastName = ""
    member this.AccountNumber
       with get() = number
       and set(value) = number <- value
    member this.FirstName
       with get() = firstName
       and set(value) = firstName <- value
    member this.LastName
       with get() = lastName
       and set(value) = lastName <- value
    member this.Balance
       with get() = balance
       and set(value) = balance <- value
    member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
    member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount


let account1 = new Account(AccountNumber=8782108, 
                           FirstName="Darren", LastName="Parker",
                           Balance=1543.33)

Para obtener más información, vea Constructores (F#).

Parámetros opcionales

Se puede especificar un parámetro opcional para un método incluyendo un signo de interrogación delante del nombre de parámetro.Los parámetros opcionales se interpretan como el tipo de opción de F#, de modo que se puedan consultar mediante la manera habitual de consultar los tipos de opción, con una expresión match con Some y None.Los parámetros opcionales solo se permiten en los miembros, no en las funciones creadas mediante enlaces let.

También se puede utilizar una función defaultArg, que establece un valor predeterminado de un argumento opcional.La función defaultArg toma el parámetro opcional como primer argumento y el valor predeterminado, como el segundo.

En el siguiente ejemplo se muestra el uso de los parámetros opcionales.

type DuplexType =
    | Full
    | Half

type Connection(?rate0 : int, ?duplex0 : DuplexType, ?parity0 : bool) =
    let duplex = defaultArg duplex0 Full
    let parity = defaultArg parity0 false
    let mutable rate = match rate0 with
                        | Some rate1 -> rate1
                        | None -> match duplex with
                                  | Full -> 9600
                                  | Half -> 4800
    do printfn "Baud Rate: %d Duplex: %A Parity: %b" rate duplex parity

let conn1 = Connection(duplex0 = Full)
let conn2 = Connection(duplex0 = Half)
let conn3 = Connection(300, Half, true)

La salida es la siguiente.

Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Baud Rate: 300 Duplex: Half Parity: true

Pasar por referencia

F# admite la palabra clave byref, que especifica que un parámetro se pasa por referencia.Esto significa que cualquier cambio del valor se conserva después de ejecutar la función.Los valores proporcionados a un parámetro byref deben ser mutables.Como alternativa, se pueden pasar celdas de referencia del tipo adecuado.

Pasar por referencia en los lenguajes .NET ha evolucionado como un modo de devolver más de un valor de una función.En F#, se puede devolver una tupla con este fin, o bien utilizarse una celda de referencia mutable como parámetro.El parámetro byref se proporciona principalmente con fines de interoperabilidad con las bibliotecas de .NET.

En los ejemplos siguientes se muestra el uso de la palabra clave byref.Tenga en cuenta que, al usar una celda de referencia como parámetro, es preciso crear una celda de referencia como valor con nombre y usarla como parámetro; no basta con agregar el operador ref como se muestra en la primera llamada a Increment del código siguiente.Dado que al crear una celda de referencia, se crea una copia del valor subyacente, la primera llamada se limita a incrementar un valor temporal.

type Incrementor(z) =
    member this.Increment(i : int byref) =
       i <- i + z

let incrementor = new Incrementor(1)
let mutable x = 10
// Not recommended: Does not actually increment the variable.
incrementor.Increment(ref x)
// Prints 10.
printfn "%d" x  

let mutable y = 10
incrementor.Increment(&y)
// Prints 11.
printfn "%d" y 

let refInt = ref 10
incrementor.Increment(refInt)
// Prints 11.
printfn "%d" !refInt  

Se puede utilizar una tupla como valor devuelto para almacenar cualquier parámetro out en los métodos de las bibliotecas de .NET.Como alternativa, se puede tratar el parámetro out como un parámetro byref.En el siguiente ejemplo código se muestran ambas posibilidades.

// TryParse has a second parameter that is an out parameter
// of type System.DateTime.
let (b, dt) = System.DateTime.TryParse("12-20-04 12:21:00")

printfn "%b %A" b dt

// The same call, using an address of operator.
let mutable dt2 = System.DateTime.Now
let b2 = System.DateTime.TryParse("12-20-04 12:21:00", &dt2)

printfn "%b %A" b2 dt2

Matrices de parámetros

En ocasiones, es necesario definir una función que toma un número arbitrario de parámetros de tipo heterogéneo.No resultaría práctico crear todos los posibles métodos sobrecargados correspondientes a todos los tipos que se podrían utilizar.La plataforma .NET proporciona compatibilidad con este tipo de métodos mediante la característica de matriz de parámetros.A un método que toma una matriz de parámetros en su signatura se le puede proporcionar un número arbitrario de parámetros.Los parámetros se colocan en una matriz.El tipo de los elementos de la matriz determina los tipos de parámetros que se pueden pasar a la función.Si se define la matriz de parámetros con Object como tipo de elemento, entonces el código de cliente puede pasar valores de cualquier tipo.

En F#, las matrices de parámetros solo se pueden definir en los métodos.No se pueden utilizar en funciones independientes ni en funciones que se definen en módulos.

Una matriz de parámetros se define mediante el atributo ParamArray.El atributo ParamArray solamente se puede aplicar al último parámetro.

En el código siguiente se muestra cómo llamar a un método de .NET que toma una matriz de parámetros, así como la definición de un tipo en F# que tiene un método que toma una matriz de parámetros.

open System

type X() =
    member this.F([<ParamArray>] args: Object[]) =
        for arg in args do
            printfn "%A" arg

[<EntryPoint>]
let main _ =
    // call a .NET method that takes a parameter array, passing values of various types
    Console.WriteLine("a {0} {1} {2} {3} {4}", 1, 10.0, "Hello world", 1u, true)

    let xobj = new X()
    // call an F# method that takes a parameter array, passing values of various types
    xobj.F("a", 1, 10.0, "Hello world", 1u, true)
    0

Cuando se ejecuta en un proyecto, la salida del código anterior es la siguiente:

a 1 10 Hello world 1 True
"a"
1
10.0
"Hello world"
1u
true

Vea también

Otros recursos

Miembros (F#)