2010-08-24 22 views
16

Esta es una pregunta bastante simple, y solo quería comprobar que lo que estoy haciendo y cómo estoy interpretando el F # tiene sentido. Si tengo la declaraciónF # Funciones vs. Valores

let printRandom = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

En lugar de crear printRandom como una función, F # ejecuta una vez y luego lo asigna un valor. Entonces, ahora, cuando llamo printRandom, en lugar de obtener un nuevo valor aleatorio e imprimirlo, simplemente obtengo lo que se devolvió la primera vez. Puedo evitar esto mi definirlo como tal:

let printRandom() = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

¿Es esta la forma correcta de establecer esta distinción entre las funciones y los valores de los parámetros-menos? Esto parece menos que ideal para mí. ¿Tiene consecuencias en currying, composición, etc.?

+2

Tenga en cuenta que casi seguramente desea agregar un 'let' delante de la primera aparición de' x'; de lo contrario, está realizando una comparación y tirando el resultado. – kvb

+0

pregunta impresionante, estaba teniendo el * exacto * mismo problema. Lamentablemente lo encontré después de descubrir la solución. – ses011

Respuesta

18

La forma correcta de ver esto es que F # no tiene funciones paramétricas. Todas las funciones tienen que tomar un parámetro, pero a veces no le importa cuál es, por lo que usa () (el valor singleton de la unidad de tipo). También puede hacer una función como esta:

let printRandom unused = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

o esto:

let printRandom _ = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

Pero () es la forma predeterminada para expresar que no se utiliza el parámetro. Expresa ese hecho a la persona que llama, porque el tipo es unit -> int no 'a -> int; así como para el lector, porque el sitio de llamadas es printRandom() no printRandom "unused".

El curado y la composición de hecho se basan en el hecho de que todas las funciones toman un parámetro y devuelven un valor.

La forma más común de escribir llamadas con la unidad, por cierto, es con un espacio, especialmente en los parientes que no son .NET de F # como Caml, SML y Haskell. Eso es porque () es un valor singleton, no una cosa sintáctica como en C#.

8

Su análisis es correcto.

La primera instancia define un valor y no una función. Admito que esto me sorprendió un par de veces cuando comencé con F # también. Viniendo de C#, parece muy natural que una expresión de asignación que contiene múltiples enunciados debe ser una lambda y, por lo tanto, demorada evaluada.

Este no es el caso en F #. Los enunciados pueden anidarse casi arbitrariamente (y es sorprendente por tener funciones y valores con alcance local). Una vez que te sientas cómodo con esto, comienzas a verlo como una ventaja, ya que puedes crear funciones y continuaciones que son inaccesibles para el resto de la función.

El segundo enfoque es la forma estándar para crear una función que lógicamente no toma argumentos. No sé la terminología precisa que usaría el equipo F # para esta declaración (tal vez una función tomando un solo argumento del tipo unit). Entonces realmente no puedo comentar cómo afectaría el currying.

+4

El currying no se aplica aquí, ya que la función solo tiene un parámetro, de tipo 'unit'. La aplicación parcial no tiene sentido: aplica completamente la función (pasando '()'), o no la llama en absoluto. –

7

¿Es esta la forma correcta de establecer esta distinción entre sin parámetros funciones y valores? Esto parece menos que ideal para mí. ¿Tiene consecuencias en currying, composición, , etc.?

Sí, lo que usted describe es correcto.

Por lo que vale, tiene una consecuencia muy interesante capaz de evaluar parcialmente las funciones en la declaración. Comparar estas dos funciones:

// val contains : string -> bool 
let contains = 
    let people = set ["Juliet"; "Joe"; "Bob"; "Jack"] 
    fun person -> people.Contains(person) 

// val contains2 : string -> bool 
let contains2 person = 
    let people = set ["Juliet"; "Joe"; "Bob"; "Jack"] 
    people.Contains(person) 

Ambas funciones producen resultados idénticos, contains crea su gente establecidos en la declaración y lo reutiliza, mientras que contains2 crea su gente establece cada vez que se llama a la función. Resultado final: contains es ligeramente más rápido. Así que conocer la distinción aquí puede ayudarte a escribir un código más rápido.

3

Los cuerpos de asignación que parecen cuerpos de funciones han desconfiado de algunos programadores. Usted puede hacer las cosas aún más interesantes por tener la asignación devuelve una función:

let foo = 
    printfn "This runs at startup" 
    (fun() -> printfn "This runs every time you call foo()") 

acabo escribió una entrada de blog sobre él en http://blog.wezeku.com/2010/08/23/values-functions-and-a-bit-of-both/.