¿Alguien tiene un ejemplo decente, preferiblemente práctico/útil, podrían publicar demostrando el concepto?F # Cierre
Respuesta
Los cierres se pueden utilizar por una serie de razones, una de las cuales es reducir el número de funciones o valores auxiliares. Entonces, en lugar de contaminar el módulo/espacio de nombres con basura al azar, puedes ponerlos al alcance justo donde sean necesarios.
open System
let isStrongPassword password =
let isLongEnough (str : string) = str.Length > 10
let containsNumber str =
str |> Seq.tryfind (fun c -> Char.IsDigit(c)) |> Option.is_some
let containsUpper str =
str |> Seq.tryfind (fun c -> Char.IsUpper(c)) |> Option.is_some
if isLongEnough password &&
containsNumber password &&
containsUpper password then
true
else
false
Editar: Así es otro ejemplo que captura los valores y los pone en un ámbito interno sin ser pasado como parámetros.
#light
open System
let isPasswordStrongerThan myPassword yourPassword =
let mineIsLongerThan (x : string) =
(myPassword.Length > x.Length)
let mineHasMoreNumsThan (x : string) =
let numDigits (x : string) =
x
|> Seq.map (Char.IsDigit)
|> Seq.fold (+) 0
(numDigits myPassword > numDigits x)
if mineIsLongerThan yourPassword && mineHasMoreNumsThan yourPassword then
true
else
false
En el ejemplo 'myPassword' se usa en todas las funciones internas, pero no se pasó como un parámetro.
El primer ejemplo de Chris Smith ofrece una visión útil de identifier scope in F#. Sin embargo, no aprovecha el bound variables disponible para las funciones anidadas. En la siguiente variación, minLength está disponible para la función interna porque es un cierre.
open System.Text.RegularExpressions
let isStrongPassword minLength =
fun (password : string) ->
password.Length >= minLength &&
Regex.IsMatch(password, "\\d") &&
Regex.IsMatch(password, "[A-Z]")
Este es un uso trivial de un cierre, ya que podría lograr lo mismo por currying en F #.
ESV - según tengo entendido (que también está limitado por una perspectiva OO), el cierre tiene que ver con la limitación de sus funciones (como isLongEnough) dentro del alcance del método que las usa. Esto efectivamente cierra (de ahí el término cierre) estas funciones se desactivan del resto del código de su aplicación.
creo que estoy entendiendo bien, si no Espero estar dos sets también;)
@Alex - eso es sólo el alcance de las funciones. cierres basados en eso sin embargo.
@ESV -
la (fun c...
forma un cierre --incluso aunque está siendo aplicada directamente y no pasa alrededor. No diría que es un gran ejemplo. Algo, más simple y directo:
let derivative dx =
(fun f x -> (f (x+dx)) - (f x)/dx)
Estamos volviendo un entorno (dx) y la función anónima. Ahora, no necesariamente tiene que devolver una función, como el ejemplo de Chris. Pero siempre tiene que ver con el uso de variables de ámbito más amplio (o vinculadas), el entorno, dentro de un contexto local.
let filter lst f_critera =
let rec loop_ = function
| hd :: tl ->
if f_critera hd then hd :: (loop_ tl)
else (loop_tl)
| [] -> []
in
loop_ lst
Por lo tanto, se trata de un cierre, aunque Forcé un poco, pero en aras de definirlos Es un marco decente. f_criteria
está vinculado, dentro de loop_
- es la variable de entorno.
@ESV, los cierres son los tres let
definiciones que se sangría (es decir. isLongEnough
, containsNumber
y containsUpper
). Son cierres porque son funciones definidas dentro del alcance de la función isStrongPassword
.
En el texto Functional Programming existe esta definición:
Los cierres son funciones que llevan alrededor de algunos de los "medio ambiente" en el que se han definido. En particular, un cierre puede hacer referencia a variables que estaban disponibles en el punto de su definición.
Esta definición probablemente no es completa, pero es fácil de comprender para alguien del lenguaje imperativo/objetivo.
Los cierres son útiles para el almacenamiento en memoria caché y la memorización. Por ejemplo:
let isWord (words: string list) =
let wordTable = Set.Create(words)
fun w -> wordTable.Contains(w)
> let isCapital = isWord ["London";"Paris";"Warsaw";"Tokyo"];;
val isCapital : (string -> bool)
> isCapital "Paris";;
val it : bool = true
> isCapital "Manchester";;
val it : bool = false
Aviso cómo el wordTable
se calcula sólo una vez, cuando se crea el cierre.
En términos más generales, los cierres son útiles para mantener un estado privado. Por lo tanto, incluso puede recrear células de cons puramente con funciones.
let cons a b = function
| true -> a
| false -> b
let car p = p true
let cdr p = p false
> (car (cons 1 2))
val it : int = 1
> (cdr (cons 1 2))
val it : int = 2
Bueno, aquí las células cons son inmutables. Pero podrías imaginar tener un estado mutable también. Vamos a hacer un pequeño mostrador:
let makeCounter() =
let x = ref 0
let tick() =
x := !x + 1
!x
tick
let c1 = makeCounter()
let c2 = makeCounter()
> c1()
val it : int = 1
> c1()
val it : int = 2
> c2()
val it : int = 1
Se sabe que los cierres son objetos de un hombre pobre, porque los objetos son cierres de un hombre pobre :) Puede simular una con la otra.
He tenido problemas con esto también, viendo la palabra "clausura" lanzada por los expertos de F #. Suena como "alcance" (y alcance anidado) que son familiares, pero en realidad tiene poco que ver con eso, y solo es relevante en el código que está pasando por las funciones como valores fuera de alcance original.
@ Robert tiene una buena cita ...
cierres son funciones que llevan alrededor de algunos de los "medio ambiente" en el que se han definido. En particular, un cierre puede hacer referencia a variables que estaban disponibles en el punto de su definición.
El mejor ejemplo que he visto ...
let Counter =
let count = ref 0
// *Return a function that carries the current context* (ie. "count", on
// the heap). Each time it is called, it will reference, and increment,
// the same location
(fun() -> incr count; !count)
> Counter()
val it : int = 1
> Counter()
val it : int = 2 <-- !! The anonymous function has retained the context
Esto sólo funciona porque recuento es una variable "ref" (es decir. En el montón), y debido a Contador devuelve una función (en vez de un entero)
el siguiente es el camino equivocado - el uso de la pila en lugar del montón ...
let Counter2 =
let mutable count = 0
// Attempt to reference mutable variable, from within a closure, is invalid
(fun() -> count <- count+1; count)
Esto produce un mensaje de error muy útil, que nos dice mucho acerca de los cierres ...
La variable variable 'count' se utiliza de forma no válida. Las variables mutables no pueden ser capturadas por cierres. Considere eliminar este uso de mutación o usar una celda de referencia mutable asignada en el montón a través de 'ref' y '!'
(felicitaciones al autor de ese mensaje!)
Syme, et al, "Experto F #", dice, en el uso de cierres ...
Ésta es una técnica poderosa para esconderse y encapsular el estado mutable sin tener que recurrir a la escritura nuevo tipo de clase y las definiciones . Es una buena práctica de programación en el código pulido a asegurarse de que todos los elementos relacionados de estado mutable se recopilan bajo alguna estructura de datos con nombre o otra entidad como una función.
El cierre se forma cuando las variables libres de una función son fijas. Esto es similar a un método de clase que está vinculado a una instancia de objeto y se refiere a las variables de miembro.
Las variables libres son variables que no se pasan a la función como parámetros, es decir, se recogen del entorno donde se define la función.
Los cierres son más útiles para los call-backs y los manejadores de eventos, ya que permiten formar un objeto temporal sin declarar uno. Esta es una gran ventaja de la programación funcional sobre OOP.
Tenga en cuenta que currying hace algo similar, pero es un concepto diferente: está fijando algunas variables enlazadas (es decir, parámetros de función). Por el contrario, el cierre arregla variables libres.
Aquí hay un ejemplo muy simple
let rememberTheEvens =
let theList = List<int>()
fun n ->
if n%2 = 0 then theList.Add(n)
theList
Y aquí está una escritura correcta hasta que hice hace un tiempo que utiliza un cierre para memoization http://www.devjoy.com/2013/05/learning-to-think-functionally-memoization/
Espero que ayude.
- 1. F consola # parada de cierre
- 2. F #: ¿Desechar los recursos que están dentro de un cierre?
- 3. Javascript cierre evaluación inmediata
- 4. Acerca del cierre de pitón
- 5. Método de invocación como cierre
- 6. f Demostrando (f bool) = bool
- 7. F # y ADO.NET - idiomático F #
- 8. Cómo utilizar Python cierre gestor de contexto
- 9. Javascript cierre
- 10. Cómo obtener F # powerpack para F # 3.0
- 11. F # - Mantener F # interactivo de publicar salida
- 12. Ruby daemon con cierre limpio
- 13. F # operator "?"
- 14. Aprendizaje F #
- 15. F # Constructor
- 16. F # string.Format
- 17. comportamiento extraño en el cierre pitón
- 18. Crear cierre desde genérico en Scala
- 19. Cierre Java InputStreams
- 20. Trazando un cierre
- 21. Cierre BufferedReader y System.in
- 22. cierre conexión WCF
- 23. Nuevo cierre en ScriptBlock
- 24. Groovy Concepto de cierre
- 25. Pydev cierre paréntesis autocompletar
- 26. Cierre/alcance JavaScript/jQuery
- 27. Javascript: cierre del ciclo?
- 28. etiqueta de cierre PHP
- 29. griales mockFor cierre wierdness
- 30. JS prototipo vs cierre
Una nota al margen: la última declaración 'if-then-else' es equivalente a' mineIsLongerThan yourPassword && mineHasMoreNumsThan yourPassword' –