Buscar..


Funciones de más de un parámetro.

En F #, todas las funciones toman exactamente un parámetro . Esto parece una declaración extraña, ya que es trivialmente fácil declarar más de un parámetro en una declaración de función:

let add x y = x + y

Pero si escribe esa declaración de función en el intérprete interactivo F #, verá que su tipo de firma es:

val add : x:int -> y:int -> int

Sin los nombres de los parámetros, esa firma es int -> int -> int . El operador -> es asociativo a la derecha, lo que significa que esta firma es equivalente a int -> (int -> int) . En otras palabras, add es una función que toma un parámetro int , y devuelve una función que toma un int y devuelve int . Intentalo:

let addTwo = add 2
// val addTwo : (int -> int)
let five = addTwo 3
// val five : int = 5

Sin embargo, también puede llamar a una función como add de una manera más "convencional", pasándole directamente dos parámetros, y funcionará como cabría esperar:

let three = add 1 2
// val three : int = 3

Esto se aplica a las funciones con tantos parámetros como desee:

let addFiveNumbers a b c d e = a + b + c + d + e
// val addFiveNumbers : a:int -> b:int -> c:int -> d:int -> e:int -> int
let addFourNumbers = addFiveNumbers 0
// val addFourNumbers : (int -> int -> int -> int -> int)
let addThreeNumbers = addFourNumbers 0
// val addThreeNumbers : (int -> int -> int -> int)
let addTwoNumbers = addThreeNumbers 0
// val addTwoNumbers : (int -> int -> int)
let six = addThreeNumbers 1 2 3  // This will calculate 0 + 0 + 1 + 2 + 3
// val six : int = 6

Este método de pensar en las funciones de parámetros múltiples como funciones que toman un parámetro y devuelven funciones nuevas (que a su vez pueden tomar un parámetro y devolver funciones nuevas, hasta que llegue a la función final que toma el parámetro final y finalmente devuelve un resultado) se llama currying , en honor al matemático Haskell Curry, famoso por desarrollar el concepto. (Fue inventado por otra persona, pero Curry merece merecidamente la mayor parte del crédito por ello).

Este concepto se usa en F #, y querrás estar familiarizado con él.

Fundamentos de funciones

La mayoría de las funciones en F # se crean con la sintaxis de let :

let timesTwo x = x * 2

Esto define una función llamada timesTwo que toma un solo parámetro x . Si ejecuta una sesión interactiva de F # ( fsharpi en OS X y Linux, fsi.exe en Windows) y pega esa función (y agrega el ;; eso le dice a fsharpi que evalúe el código que acaba de escribir), verá que responde con:

val timesTwo : x:int -> int

Esto significa que timesTwo es una función que toma un solo parámetro x de tipo int , y devuelve un int . Las firmas de funciones a menudo se escriben sin los nombres de los parámetros, por lo que a menudo verá este tipo de función escrito como int -> int .

¡Pero espera! ¿Cómo supo F # que x era un parámetro entero, ya que nunca especificaste su tipo? Eso se debe a la inferencia de tipos . Debido a que en el cuerpo de la función, usted multiplicó x por 2 , los tipos de x y 2 deben ser iguales. (Como regla general, F # no convertirá implícitamente los valores a diferentes tipos; debe especificar explícitamente las tipografías que desee).

Si desea crear una función que no tome ningún parámetro, esta es la forma incorrecta de hacerlo:

let hello =  // This is a value, not a function
    printfn "Hello world"

La forma correcta de hacerlo es:

let hello () =
    printfn "Hello world"

Esta función de hello tiene el tipo de unit -> unit , que se explica en el tipo de "unidad" .

Funciones Curried vs Tupled

Hay dos formas de definir funciones con múltiples parámetros en F #, funciones Curried y funciones Tupled.

let curriedAdd x y = x + y // Signature: x:int -> y:int -> int
let tupledAdd (x, y) = x + y // Signature:  x:int * y:int -> int

Todas las funciones definidas desde fuera de F # (como el marco .NET) se utilizan en F # con la forma de Agrupación. La mayoría de las funciones en los módulos de núcleo F # se utilizan con la forma Curried.

La forma al curry se considera F # idiomática, porque permite una aplicación parcial. Ninguno de los dos ejemplos siguientes son posibles con la forma Agrupada.

let addTen = curriedAdd 10 // Signature: int -> int

// Or with the Piping operator
3 |> curriedAdd 7 // Evaluates to 10

La razón detrás de esto es que la función Curried, cuando se llama con un parámetro, devuelve una función. Bienvenido a la programación funcional !!

let explicitCurriedAdd x = (fun y -> x + y) // Signature: x:int -> y:int -> int
let veryExplicitCurriedAdd = (fun x -> (fun y -> x + y)) // Same signature

Puedes ver que es exactamente la misma firma.

Sin embargo, al interactuar con otro código .NET, como cuando se escriben bibliotecas, es importante definir las funciones utilizando el formulario Agrupado.

En línea

Alinear le permite reemplazar una llamada a una función con el cuerpo de la función.

Esto a veces es útil por razones de rendimiento en una parte crítica del código. Pero la contraparte es que su ensamblaje ocupará mucho espacio ya que el cuerpo de la función se duplica en todos los lugares en que se produjo una llamada. Debe tener cuidado al decidir si desea integrar una función o no.

Una función se puede alinear con la palabra clave en inline :

// inline indicate that we want to replace each call to sayHello with the body 
// of the function
let inline sayHello s1 =
    sprintf "Hello %s" s1

// Call to sayHello will be replaced with the body of the function
let s = sayHello "Foo"
// After inlining -> 
// let s = sprintf "Hello %s" "Foo"

printfn "%s" s
// Output
// "Hello Foo"

Otro ejemplo con valor local:

let inline addAndMulti num1 num2 =
    let add = num1 + num2
    add * 2

let i = addAndMulti 2 2
// After inlining ->
// let add = 2 + 2
// let i = add * 2

printfn "%i" i
// Output
// 8

Pipa hacia adelante y hacia atrás

Los operadores de tuberías se utilizan para pasar parámetros a una función de una manera simple y elegante. Permite eliminar valores intermedios y hacer que las llamadas a funciones sean más fáciles de leer.

En F #, hay dos operadores de tubería:

  • Adelante ( |> ): pasando parámetros de izquierda a derecha

     let print message =
         printf "%s" message
     
     // "Hello World" will be passed as a parameter to the print function
     "Hello World" |> print
    
  • Atrás ( <| ): pasar parámetros de derecha a izquierda

     let print message =
         printf "%s" message
     
     // "Hello World" will be passed as a parameter to the print function
     print <| "Hello World"
    

Aquí hay un ejemplo sin operadores de tubería:

// We want to create a sequence from 0 to 10 then:
// 1 Keep only even numbers
// 2 Multiply them by 2
// 3 Print the number

let mySeq = seq { 0..10 }
let filteredSeq = Seq.filter (fun c -> (c % 2) = 0) mySeq
let mappedSeq = Seq.map ((*) 2) filteredSeq
let finalSeq = Seq.map (sprintf "The value is %i.") mappedSeq

printfn "%A" finalSeq

Podemos acortar el ejemplo anterior y hacerlo más limpio con el operador de tubería delantera:

// Using forward pipe, we can eliminate intermediate let binding
let finalSeq = 
    seq { 0..10 }
    |> Seq.filter (fun c -> (c % 2) = 0)
    |> Seq.map ((*) 2)
    |> Seq.map (sprintf "The value is %i.")

printfn "%A" finalSeq

El resultado de cada función se pasa como un parámetro a la siguiente función.

Si desea pasar varios parámetros al operador de tubería, debe agregar un | para cada parámetro adicional y crear una tupla con los parámetros. El operador de tubería F # nativo admite hasta tres parámetros (|||> o <|||).

let printPerson name age =
    printf "My name is %s, I'm %i years old" name age

("Foo", 20) ||> printPerson


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow