¿Cuál es la diferencia entre "cerrar" y "lambda"?

¿Alguien puede explicar? Entiendo los conceptos básicos detrás de ellos, pero a menudo veo que se usan indistintamente y me siento avergonzado.

Y ahora que estamos aquí, ¿en qué se diferencian de la función habitual?

645
21 окт. establecer sker 21 de octubre 2008-10-21 06:12 '08 a las 6:12 2008-10-21 06:12
@ 10 respuestas

Un lambda es solo una función anónima, una función definida sin nombre. En algunos idiomas, como Scheme, son equivalentes a las funciones nombradas. De hecho, la definición de la función se reescribe como un enlace de lambda a la variable que está dentro. En otros idiomas, como Python, hay algunas diferencias (en lugar de redundantes) entre ellos, pero se comportan de la misma manera diferente.

Un cierre es cualquier función que cierra el entorno en el que se definió. Esto significa que puede acceder a variables que no están en la lista de parámetros. Ejemplos:

 def func(): return h def anotherfunc(h): return func() 

Esto causará un error, porque func no cierra el entorno en otra anotherfunc - h es igual a indefinido. func cerrada solo en el entorno global. Esto funcionará:

 def anotherfunc(h): def func(): return h return func() 

Debido a que aquí la func se define en anotherfunc , y en python 2.3 y superior (o algún número como este), cuando casi consiguen los cierres correctos (la mutación aún no funciona), esto significa que cierra el entorno de otro funcionamiento y puede acceder a las variables dentro de ella En Python 3.1+, la mutación también funciona cuando se usa la nonlocal .

Otro punto importante: la func continuará anotherfunc miércoles, incluso si ya no se valora en anotherfunc . Este código también funcionará:

 def anotherfunc(h): def func(): return h return func print anotherfunc(10)() 

Esto imprimirá 10.

Esto, como ha notado, no tiene nada que ver con lambda s: estos son dos conceptos diferentes (aunque relacionados).

584
21 окт. Claudiu respondió el 21 de octubre. 2008-10-21 06:58 '08 a las 6:58 am 2008-10-21 06:58

Cuando la mayoría de la gente piensa en funciones, piensa en las funciones nombradas :

 function foo() { return "This string is returned from the 'foo' function"; } 

Se llaman por su nombre, por supuesto:

 foo(); //returns the string above 

Con las expresiones lambda, puedes tener funciones anónimas :

  @foo = lambda() {return "This is returned from a function without a name";} 

En el ejemplo anterior, puede llamar a lambda a través de la variable a la que fue asignado:

 foo(); 
border=0

Más útil que asignar funciones anónimas a variables, pasarlas o desde funciones de orden superior, es decir, Funciones que aceptan / devuelven otras funciones. En muchos casos, el uso de la función no es obligatorio:

 function filter(list, predicate) { @filteredList = []; for-each (@x in list) if (predicate(x)) filteredList.add(x); return filteredList; } //filter for even numbers filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

El cierre puede ser una función con nombre o anónima, pero se conoce como tal cuando "cierra" las variables en el área donde se define la función, es decir, el cierre aún se aplica al entorno con cualquier variable externa que se utilice en el cierre en sí. Aquí está el cierre nombrado:

 @x = 0; function incrementX() { x = x + 1;} incrementX(); // x now equals 1 

No parece mucho, pero ¿qué pasa si todo está en una función diferente, y usted pasa la función externa al incrementX ?

 function foo() { @x = 0; function incrementX() { x = x + 1; return x; } return incrementX; } @y = foo(); // y = closure of incrementX over foo.x y(); //returns 1 (yx == 0 + 1) y(); //returns 2 (yx == 1 + 1) 

Así obtienes objetos con preservación de estado en programación funcional. Como el nombre "incrementX" no es obligatorio, puede usar lambda en este caso:

 function foo() { @x = 0; return lambda() { x = x + 1; return x; }; } 
161
21 окт. respuesta dada por Mark Cidade 21 de octubre 2008-10-21 06:46 '08 a las 6:46 am 2008-10-21 06:46

Hay mucha confusión en torno a lambda y el cierre, incluso en las respuestas a esta pregunta de StackOverflow. En lugar de preguntar a los programadores aleatorios que han aprendido acerca de cerrar la práctica con ciertos lenguajes de programación u otros programadores desconocidos, vaya a la fuente (donde comenzó todo). Y como la lambda y las puertas provienen del Cálculo de Lambda , inventado por la Iglesia Alonso en los años 30 antes de que aparecieran las primeras computadoras electrónicas, esta es la fuente de la que estoy hablando.

Lambda Calculus es el lenguaje de programación más fácil del mundo. Lo único que puedes hacer en esto:

  • APLICACIÓN: aplicando una expresión a otra, denotado fx .
    (Piense en ello como una llamada de función, donde f es una función x es el único parámetro)
  • ABSTRACCIÓN: Enlaza un carácter que aparece en una expresión para indicar que este carácter es solo una "ranura", un campo vacío que espera llenar el valor, como una "variable". Esto se hace agregando la letra griega λ (lambda), luego un nombre simbólico (por ejemplo, x ) y luego a . . Esta expresión luego convierte la expresión en una función que espera un parámetro.
    Por ejemplo: λx.x+2 acepta la expresión x+2 e informa que el carácter x en esta expresión es una variable vinculada, se puede reemplazar con el valor que especifique como parámetro.
    Tenga en cuenta que la función definida de esta manera es anónima: no tiene nombre, por lo que aún no puede referirse a ella, pero puede llamarla de inmediato (¿recuerda la aplicación?), Configurando el parámetro que está esperando, por ejemplo: (λx.x+2) 7 . Luego, la expresión (en este caso, el valor literal) 7 se reemplaza por x en la subexpresión x+2 la lambda utilizada, de modo que obtiene 7+2 , que luego se reduce a 9 acuerdo con las reglas generales de la aritmética.

Entonces, decidimos uno de los secretos:
lambda es la función anónima del ejemplo anterior, λx.x+2 .


Diferentes lenguajes de programación pueden tener una sintaxis diferente para la abstracción funcional (lambda). Por ejemplo, en JavaScript se ve así:
 function(x) { return x+2; } 

e inmediatamente puedes aplicarlo a algún parámetro:

 function(x) { return x+2; } (7) 

o puede guardar esta función anónima (lambda) en alguna variable:

 var f = function(x) { return x+2; } 

que en realidad le da el nombre f , lo que le permite referirse a él y llamarlo varias veces después, por ejemplo:

 alert( f(7) + f(10) ); // should print 21 in the message box 

Pero no tenías que llamarlo. Inmediatamente puede llamarlo:

 alert( function(x) { return x+2; } (7) ); // should print 9 in the message box 

En LISP, las lambdas se crean de la siguiente manera:

 (lambda (x) (+ x 2)) 

y puede llamar a ese lambda aplicándolo inmediatamente al parámetro:

 ( (lambda (x) (+ x 2)) 7 ) 

<h / "> Ok, ahora es el momento de resolver otro misterio: qué es el cierre. Para hacer esto, permítanme hablar sobre los símbolos (variables) en las expresiones lambda.

Como dije, lo que hace la abreviatura de lambda es un carácter requerido en su subexpresión, de modo que se convierte en un parámetro reemplazable. Tal símbolo se llama atado. ¿Pero qué pasa si hay otros caracteres en la expresión? Por ejemplo: λx.x/y+2 . En esta expresión, el símbolo x está asociado con la abreviatura lambda λx. precediéndolo Pero el otro carácter y no y vinculado, es libre. No sabemos qué es ni de dónde viene, por lo que no sabemos qué significa ni qué valor representa, y por lo tanto no podemos evaluar esta expresión hasta que descubramos qué significa y .

De hecho, lo mismo sucede con otros dos símbolos: 2 y + . Estamos tan familiarizados con estos dos caracteres que generalmente olvidamos que la computadora no los conoce, y necesitamos decir qué significan al definirlos en algún lugar, por ejemplo. En la biblioteca o en la propia lengua.

Puede pensar en caracteres libres definidos en otro lugar fuera de la expresión, en su "contexto circundante", que se llama su entorno . El ambiente puede ser una gran expresión de la cual esta expresión forma parte (como dijo Qui-Gon Jinn: "Siempre hay un pez gordo";)), o en alguna biblioteca, o en el mismo idioma (como primitivo).

Esto permite que las expresiones lambda se dividan en dos categorías:

  • Expresiones CERRADAS: cada carácter que aparece en estas expresiones está vinculado por alguna abstracción lambda. En otras palabras, son autosuficientes; no requieren una evaluación del contexto circundante. También se les llama combinadores.
  • Expresiones ABIERTAS: algunos caracteres de estas expresiones no están relacionados, es decir, algunos de los caracteres que se encuentran en ellas son gratuitos y requieren información externa, por lo que no se pueden evaluar hasta que proporcione las definiciones de estos caracteres.

Puede CERRAR una expresión lambda abierta proporcionando un entorno que defina todos estos caracteres libres, vinculándolos con algunos valores (que pueden ser números, cadenas, funciones anónimas, también llamadas lambdas, lo que sea ...).

Y aquí está la parte final:
El cierre de una expresión lambda es un conjunto específico de caracteres definidos en un contexto externo (entorno) que da valores a los caracteres libres en esta expresión, lo que los hace más libres y no libres. Convierte una expresión lambda abierta, que aún contiene algunos caracteres libres "indefinidos", en una cerrada que ya no tiene caracteres libres.

Por ejemplo, si tiene la siguiente expresión lambda: λx.x/y+2 , x está relacionada e y libre, por lo que la expresión está open y no se puede evaluar a menos que diga qué significa y (y lo mismo c + y 2 , que también son gratis). Pero supongamos que usted también tiene ese entorno:

 { y: 3, +: [built-in addition], 2: [built-in number], q: 42, w: 5 } 

Este entorno proporciona definiciones para todos los caracteres "indefinidos" (libres) de nuestras expresiones lambda ( y , + , 2 ) y algunos caracteres adicionales ( q , w ). Los caracteres que debemos definir son un subconjunto del entorno:

 { y: 3, +: [built-in addition], 2: [built-in number] } 

y esto es solo el cierre de nuestras expresiones lambda:>

En otras palabras, cierra la expresión lambda abierta. Fue aquí donde se cerró el nombre y, por lo tanto, muchas de las respuestas en este hilo no son del todo correctas: P


Entonces, ¿por qué están equivocados? ¿Por qué muchos de ellos dicen que los cierres son algunas estructuras de datos en la memoria o algunas características de los lenguajes que usan, o por qué confunden los cierres con las lambdas?: P

Bueno, marketoids corporativos Sun / Oracle, Microsoft, Google, etc. culpa porque llamaron a estos constructos en sus propios lenguajes (Java, C #, Go, etc.). A menudo se les llama "cierres", que simplemente deben ser lambdas. O llaman "cierre" a una técnica específica que utilizaron para implementar el alcance léxico, es decir, El hecho de que una función puede acceder a las variables que se han definido en el ámbito externo en el momento de su definición. A menudo dicen que la función "rodea" estas variables, es decir, las captura en alguna estructura de datos para evitar que se destruyan una vez que se complete la función externa. Pero este es el post factum "etimología del folklore" y el marketing, que solo complica las cosas, porque cada proveedor de idiomas usa su propia terminología.

Y esto es aún peor debido al hecho de que en lo que dicen, siempre hay una pequeña verdad que no te permite descartarla fácilmente como falsa: P Déjame explicarte:

Si desea implementar un lenguaje que use lambda como ciudadanos de primera clase, debe permitirles usar los símbolos definidos en el contexto que los rodea (es decir, usar variables libres en sus lambdas). Y estos personajes deben estar allí, incluso cuando la función que la rodea vuelve. El problema es que estos caracteres están vinculados a algún almacenamiento de función local (generalmente en la pila de llamadas), que ya no estará allí cuando la función regrese. Por lo tanto, para que lambda funcione como usted espera, necesita de alguna manera "capturar" todas estas variables libres de su contexto externo y guardarlas para más tarde, incluso cuando el contexto externo desaparece. Es decir, debe encontrar el cierre de su lambda (todas estas variables externas que utiliza) y almacenarlo en otro lugar (ya sea creando una copia o preparando el espacio para ellos, en otro lugar que no sea en la pila). ). El método real que utiliza para lograr este objetivo es el "detalle de implementación" de su idioma. Lo importante aquí es el cierre, que es un conjunto de variables libres de su entorno lambda que deben guardarse en algún lugar.

A las personas les tomó mucho tiempo comenzar a llamar a la estructura de datos real que utilizan en sus implementaciones de lenguaje para implementar el cierre como el "cierre" en sí. La estructura usualmente se ve así:

 Closure { [pointer to the lambda function machine code], [pointer to the lambda function environment] } 

y estas estructuras de datos se pasan como parámetros a otras funciones, se devuelven desde funciones y se almacenan en variables para representar las lambdas y permitirles acceder a su entorno, así como el código de máquina para ejecutarse en este contexto. Pero esta es solo una forma (una de muchas) de implementar el cierre, no el cierre en sí mismo.

Como expliqué anteriormente, el cierre de una expresión lambda es un subconjunto de definiciones en su entorno que da valores a las variables libres contenidas en esta expresión lambda, cerrando efectivamente la expresión (convirtiendo una expresión lambda abierta que no se puede evaluar, pero en una expresión lambda cerrada que luego se puede evaluar, ya que todos los caracteres que contiene están ahora definidos).

Todo lo demás es solo un "culto de carga" y una "magia mágica" de programadores y proveedores de idiomas que no conocen las raíces reales de estos conceptos.

Espero que responda a sus preguntas. Pero si tiene alguna pregunta de seguimiento, no dude en preguntarlas en los comentarios, y trataré de explicarlo mejor.

124
27 апр. La respuesta se da SasQ 27 de abril. 2016-04-27 04:18 '16 a las 4:18 2016-04-27 04:18

No todos los cierres son lambdas, y no todos los cierres son cierres. Ambas son funciones, pero no necesariamente en la forma en que solíamos saber.

Lambda es esencialmente una función que se define como un método integrado, no un método estándar para declarar funciones. Lambdas a menudo se pueden pasar como objetos.

El cierre es una función que rodea su estado circundante, refiriéndose a los campos externos a su cuerpo. El estado anidado permanece en la forma de una llamada de cierre.

En un lenguaje orientado a objetos, el cierre generalmente se proporciona a través de objetos. Sin embargo, algunos idiomas OO (por ejemplo, C #) implementan funciones especiales que están más cerca de la definición de cierres proporcionados por lenguajes puramente funcionales (como lisp) que no tienen objetos para habilitar el estado.

Curiosamente, la introducción de Lambdas y Closures en C # lleva a la programación funcional más cercana al uso básico.

49
21 окт. respuesta dada por Michael Brown el 21 de octubre 2008-10-21 06:29 '08 a las 6:29 2008-10-21 06:29

Es tan simple: lambda es una construcción de lenguaje, es decir, solo sintaxis para funciones anónimas; un cierre es una forma de implementarlo, o cualquier función de primera clase, para el caso, se llama o anónima.

Más precisamente, el cierre es cómo se representa la función de la función de primera clase en tiempo de ejecución, como un par de su "código" y el "cierre" del entorno para todas las variables no locales utilizadas en este código. Por lo tanto, estas variables aún están disponibles, incluso cuando las áreas externas en las que ocurren ya están completas.

Desafortunadamente, hay muchos idiomas que no admiten funciones como valores de primera clase o solo las admiten en forma paralizada. Por lo tanto, las personas a menudo usan el término "cierre" para distinguir lo "real".

13
19 марта '14 в 11:31 2014-03-19 11:31 La respuesta la da Andreas Rossberg el 19 de marzo de 2014 a las 11:31 2014-03-19 11:31

Desde el punto de vista de los lenguajes de programación, son dos cosas completamente diferentes.

Básicamente, para el lenguaje Turing completo, solo necesitamos elementos muy limitados, por ejemplo. Abstracciones, aplicaciones y abreviaturas. La abstracción y la aplicación proporcionan una forma de desarrollar la expresión lamdba, y la reducción drena el significado de la expresión lambda.

Lambda proporciona una manera de abstraer el proceso de cálculo. por ejemplo, para calcular la suma de dos números, se puede abstraer un proceso que toma dos parámetros x, y y devuelve x + y. En el diagrama, puedes escribirlo como

 (lambda (xy) (+ xy)) 

Puede cambiar el nombre de los parámetros, pero la tarea que completa no cambia. En casi todos los lenguajes de programación, puede asignar a una expresión lambda un nombre, llamado funciones. Pero no hay mucha diferencia, se pueden ver conceptualmente como simplemente azúcar sintáctica.

Bien, ahora imagina cómo se puede implementar esto. Cada vez que aplicamos una expresión lambda a algunas expresiones, por ejemplo

 ((lambda (xy) (+ xy)) 2 3) 

Simplemente podemos sustituir los parámetros con una expresión que necesita ser evaluada. Este modelo ya es muy potente. Pero este modelo no nos permite cambiar el significado de los caracteres, por ejemplo. No podemos simular un cambio de estado. Por lo tanto, necesitamos un modelo más complejo. Para hacerlo corto, siempre que queramos calcular el valor de la expresión lambda, ponemos un par de caracteres y el valor correspondiente en el medio (o tabla). Luego, el resto (+ xy) se evalúa buscando los caracteres correspondientes en la tabla. Ahora, si proporcionamos algunas primitivas para uso directo en el entorno, ¡podemos simular cambios de estado!

Con este fondo, echa un vistazo a esta función:

 (lambda (xy) (+ xyz)) 

Sabemos que cuando evaluamos una expresión lambda, xy estará enlazado en una nueva tabla. Pero ¿cómo y dónde podemos mirar? De hecho, z se llama variable libre. Debe haber un entorno externo que contenga z. De lo contrario, el valor de la expresión no se puede determinar solo por el enlace de x e y. Para aclarar esto, puedes escribir algo en el diagrama:

 ((lambda (z) (lambda (xy) (+ xyz))) 1) 

Por lo tanto, z se unirá a 1 en la tabla exterior. Todavía tenemos una función que toma dos parámetros, pero el significado real de esto también depende del entorno externo. En otras palabras, el entorno externo está cerrado en las variables libres. Con set!, Podemos hacer una función con estado, es decir, Esta no es una función en el sentido de las matemáticas. Lo que devuelve depende no solo de la entrada, sino también de z.

Esto es algo que ya sabe muy bien, el método de los objetos casi siempre depende del estado de los objetos. Es por esto que algunas personas dicen que "cerrar es un mal objeto humano". Pero también podríamos ver los objetos como personas cerradas, ya que realmente nos gustan las características de primera clase.

Utilizo un esquema para ilustrar las ideas asociadas con este esquema, es uno de los primeros idiomas que tienen cierres reales. Все материалы здесь намного лучше представлены в главе 3 SICP.