significado programacion matematica hair español closure clausura functional-programming closures

functional programming - programacion - ¿Cuál es la definición exacta de un cierre?



closures swift (8)

He leído temas anteriores sobre cierres en stackflow y otras fuentes y una cosa todavía me confunde. De lo que he podido ensamblar técnicamente, un cierre es simplemente el conjunto de datos que contienen el código de una función y el valor de las variables vinculadas en esa función.

En otras palabras, técnicamente, la siguiente función de C debería ser un cierre de mi entendimiento:

int count() { static int x = 0; return x++; }

Sin embargo, todo lo que leo parece implicar que los cierres deben implicar de algún modo funciones de paso como objetos de primera clase. Además, por lo general, parece implicarse que los cierres no son parte de la programación de procedimientos. ¿Es este un caso de una solución que está demasiado asociada con el problema que resuelve o estoy malinterpretando la definición exacta?


Creo que Peter Eddy tiene razón, pero el ejemplo podría ser más interesante. Podría definir dos funciones que se cierran sobre una variable local, incremento y decremento. El contador sería compartido entre ese par de funciones, y único para ellos. Si define un nuevo par de funciones de incremento / decremento, compartirían un contador diferente.

Además, no es necesario que pase ese valor inicial de x, podría dejar que el valor predeterminado sea cero dentro del bloque de funciones. Eso dejaría claro que está utilizando un valor al que ya no tiene acceso normal.


Gran pregunta Dado que uno de los principios de OOP de OOP es que los objetos tienen comportamiento y también datos, los cierres son un tipo especial de objeto porque su propósito más importante es su comportamiento. Dicho esto, ¿qué quiero decir cuando hablo de su "comportamiento"?

(Mucho de esto proviene de "Groovy in Action" de Dierk Konig, que es un libro impresionante)

En el nivel más simple, un cierre es en realidad solo un código que se envuelve para convertirse en un objeto / método andrógino. Es un método porque puede tomar parámetros y devolver un valor, pero también es un objeto en el que puede pasar una referencia a él.

En palabras de Dierk, imagine un sobre que tenga un pedazo de papel dentro. Un objeto típico tendría variables y sus valores escritos en este documento, pero un cierre tendría una lista de instrucciones en su lugar. Digamos que la carta dice: "Entregue este sobre y una carta a sus amigos".

In Groovy: Closure envelope = { person -> new Letter(person).send() } addressBookOfFriends.each (envelope)

El objeto de cierre aquí es el valor de la variable de envolvente y su uso es que es un parámetro para cada método.

Algunos detalles: Alcance: El alcance de un cierre es la información y los miembros a los que se puede acceder dentro de él. Regresando de un cierre: Los cierres a menudo usan un mecanismo de devolución de llamada para ejecutar y regresar de sí mismo. Argumentos: Si el cierre necesita tomar solo 1 parámetro, Groovy y otros langs proporcionan un nombre predeterminado: "it", para hacer que la codificación sea más rápida. Así, por ejemplo, en nuestro ejemplo anterior:

addressBookOfFriends.each (envelope) is the same as: addressBookOfFriends.each { new Letter(it).send() }

Espero que esto es lo que estás buscando!


No, eso no es un cierre. Su ejemplo es simplemente una función que devuelve el resultado de incrementar una variable estática.

Así es como funcionaría un cierre:

function makeCounter( int x ) { return int counter() { return x++; } } c = makeCounter( 3 ); printf( "%d" c() ); => 4 printf( "%d" c() ); => 5 d = makeCounter( 0 ); printf( "%d" d() ); => 1 printf( "%d" c() ); => 6

En otras palabras, las diferentes invocaciones de makeCounter () producen diferentes funciones con su propia vinculación de variables en su entorno léxico que han "cerrado".

Edición: Creo que ejemplos como este hacen que los cierres sean más fáciles de entender que las definiciones, pero si desea una definición, diría: "Un cierre es una combinación de una función y un entorno. El entorno contiene las variables que se definen en la función así como aquellos que son visibles para la función cuando se creó. Estas variables deben permanecer disponibles para la función mientras exista la función ".


Para la en.wikipedia.org/wiki/Closure_%28computer_science%29 . Es especialmente bueno. Solo quiero aclararlo con un ejemplo.

Suponga este fragmento de código C # (que se supone que debe realizar una búsqueda AND en una lista):

List<string> list = new List<string> { "hello world", "goodbye world" }; IEnumerable<string> filteredList = list; var keywords = new [] { "hello", "world" }; foreach (var keyword in keywords) filteredList = filteredList.Where(item => item.Contains(keyword)); foreach (var s in filteredList) // closure is called here Console.WriteLine(s);

Es un error común en C # hacer algo así. Si observa la expresión lambda dentro de Where , verá que define una función y que su comportamiento depende del valor de una variable en su sitio de definición. Es como pasar una variable a la función, en lugar del valor de esa variable . Efectivamente, cuando se llama a este cierre, recupera el valor de la variable de keyword en ese momento. El resultado de esta muestra es muy interesante. Imprime tanto "hola mundo" como "adiós mundo", que no es lo que queríamos. ¿Que pasó? Como dije anteriormente, la función que declaramos con la expresión lambda es una variable de cierre sobre keyword , así que esto es lo que sucede:

filteredList = filteredList.Where(item => item.Contains(keyword)) .Where(item => item.Contains(keyword));

y en el momento de la ejecución del cierre, la keyword tiene el valor "mundo", por lo que básicamente estamos filtrando la lista un par de veces con la misma palabra clave. La solucion es:

foreach (var keyword in keywords) { var temporaryVariable = keyword; filteredList = filteredList.Where(item => item.Contains(temporaryVariable)); }

Dado que la variable temporaryVariable está orientada al cuerpo del bucle foreach , en cada iteración, es una variable diferente. En efecto, cada cierre se vinculará a una variable distinta (son instancias diferentes de la variable temporaryVariable en cada iteración). Esta vez, dará los resultados correctos ("hola mundo"):

filteredList = filteredList.Where(item => item.Contains(temporaryVariable_1)) .Where(item => item.Contains(temporaryVariable_2));

en el que temporaryVariable_1 tiene el valor de "hello" y temporaryVariable_2 tiene el valor "world" en el momento de la ejecución del cierre.

Tenga en cuenta que los cierres han provocado una extensión de la vida útil de las variables (se suponía que su vida terminaría después de cada iteración del bucle). Este es también un importante efecto secundario de los cierres.


Por lo que entiendo, un cierre también tiene que tener acceso a las variables en el contexto de la llamada. Los cierres suelen estar asociados a la programación funcional. Los lenguajes pueden tener elementos de diferentes tipos de perspectivas de programación, funcional, de procedimiento, imperativo, declarativo, etc. Obtienen su nombre al cerrarse en un contexto específico. También pueden tener un enlace léxico, ya que pueden hacer referencia al contexto especificado con los mismos nombres que se utilizan en ese contexto. Su ejemplo no tiene ninguna referencia a ningún otro contexto que no sea uno estático global.

De Wikipedia

Un cierre se cierra sobre las variables libres (variables que no son variables locales)


Un cierre es una técnica de implementación para representar procedimientos / funciones con el estado local. Una forma de implementar cierres se describe en SICP. Voy a presentar la esencia de la misma, de todos modos.

Todas las expresiones, incluidas las funciones, se evalúan en un entorno . Un entorno es una secuencia de marcos . Un marco asigna nombres de variables a valores. Cada cuadro también tiene un puntero a su entorno envolvente. Una función se evalúa en un nuevo entorno con un marco que contiene enlaces para sus argumentos. Ahora veamos el siguiente escenario interesante. Imagina que tenemos una función llamada acumulador , que cuando se evalúa, devolverá otra función:

// This is some C like language that has first class functions and closures. function accumulator(counter) { return (function() { return ++counter; }); }

¿Qué pasará cuando evaluemos la siguiente línea?

accum1 = accumulator(0);

Primero se crea un nuevo entorno y un objeto entero (para el contador ) se vincula a 0 en su primer fotograma. El valor devuelto, que es una nueva función, está vinculado en el entorno global. Por lo general, el nuevo entorno se recolectará cuando finalice la evaluación de la función. Aquí eso no va a suceder. cum1 es una referencia a él, ya que necesita acceso al contador variable. Cuando se llama a acumulación1 , incrementará el valor de contador en el entorno al que se hace referencia. Ahora podemos llamar a acumulación1 una función con estado local o un cierre.

He descrito algunos usos prácticos de los cierres en mi blog http://vijaymathew.wordpress.com . (Consulte las publicaciones "Diseños peligrosos" y "Al pasar el mensaje").


Un objeto es el estado más la función. Un cierre, es función más estado.

la función f es un cierre cuando se cierra sobre (capturado) x


Ya hay muchas respuestas, pero agregaré otra a cualquiera ...

Los cierres no son exclusivos de los lenguajes funcionales. Ocurren en Pascal (y familia), por ejemplo, que tiene procedimientos anidados. El estándar C no los tiene (todavía), pero IIRC tiene una extensión GCC.

El problema básico es que un procedimiento anidado puede referirse a variables definidas en su padre. Además, el padre puede devolver una referencia al procedimiento anidado a su interlocutor.

El procedimiento anidado aún se refiere a las variables que eran locales para el padre, específicamente a los valores que tenían esas variables cuando se ejecutó la línea que hacía la referencia a la función, aunque esas variables ya no existan a medida que el padre haya salido.

El problema ocurre incluso si el procedimiento nunca se devuelve del padre: las diferentes referencias al procedimiento anidado construido en diferentes momentos pueden estar usando diferentes valores pasados ​​de las mismas variables.

La resolución a esto es que cuando se hace referencia a la función anidada, se empaqueta en un "cierre" que contiene los valores variables que necesita para más adelante.

Un lambda Python es un ejemplo simple de estilo funcional ...

def parent () : a = "hello" return (lamda : a) funcref = parent () print funcref ()

Mis pitones están un poco oxidados, pero creo que eso es correcto. El punto es que la función anidada (la lambda) aún se está refiriendo al valor de la variable local a , aunque el parent haya salido cuando se llama. La función necesita un lugar para preservar ese valor hasta que sea necesario, y ese lugar se llama un cierre.

Un cierre es un poco como un conjunto implícito de parámetros.