2009-02-26 11 views
35

Acabo de toparme con esto mientras reviso la fuente del compilador de Erlang.¿Qué es "Elevación Lambda"?

No lo estoy entendiendo. (vaya figura;)), teniendo en cuenta que me acabo de dar cuenta de que hay tal cosa hace 5 minutos).

Perdónenme por preguntar primero sin antes tratar de comprender las razones de su existencia.

Hay un wikipedia article al respecto, pero es bastante críptico.

+1

Lea acerca de los cierres primero - http://en.wikipedia.org/wiki/Closure_(computer_science) – dirkgently

Respuesta

42

La elevación Lambda se utiliza para convertir un cierre en una función pura. Al pasar argumentos adicionales a la función, se reduce el número de sus variables libres. A medida que "eleva" la lambda a ámbitos cada vez más altos, agrega argumentos para acomodar las variables locales declaradas en ese ámbito (que de lo contrario serían variables libres). Una vez que la lambda no tiene variables libres, es una función pura de "nivel superior".

Por supuesto, solo puede hacer esto si conoce todos los sitios de llamadas de lambda; en otras palabras, solo si la lambda no escapa.

El beneficio en un optimizador de compilador es que los cierres (entornos de funciones) se pueden eliminar. Esto podría permitir pasar los argumentos en registros en lugar de apilarlos (o acumularlos) asignándolos como variables libres.

+0

por lo que una variable libre en el sentido de los cierres es una que proviene de su entorno léxico (¿alcance de su definición?). una función de nivel superior es (en caso de erlangs) una función de módulo? ¿Qué pasa con una variable independiente que ya está en una función de nivel de módulo, también es gratuita? gracias por la respuesta detallada – deepblue

+0

Sí, sí, quizás. –

+0

Acabo de entender por qué acelera las cosas, se dio cuenta después del simple ejemplo de JS. Lo mencionaste también. Todavía no entiendo por qué tienes que saber todos los sitios de llamadas de lambda para realizar el levantamiento ... ¿no debería ser solo el env de lex en el que se definía la materia (ya que de ahí provienen los vars libres)? – deepblue

0

La elevación lambda básicamente elimina las variables y las pone en funciones puras, simplificando la ejecución.

+0

¿Por qué eso simplifica la ejecución? Lo siento por la pregunta contundente. El artículo de Wikipedia menciona lo mismo, a saber, que el propósito del levantamiento es acelerar las cosas. Todas las pequeñas cosas apuntan en la dirección correcta. gracias – deepblue

+0

@deepblue (+4 años después) porque no hay necesidad de mantener el entorno que aloja los valores de las variables libres; todas las variables libres se convierten en argumentos, por lo que solo tenemos que tratar los argumentos para evaluar/ejecutar la función. mantener el entorno, mantenerlo activo mucho después de que se abandonara el ámbito en el que se creó el cierre, es/podría ser una tarea compleja, según el idioma. P.ej. en Scheme es muy complejo. –

36

La elevación lambda es una técnica para "elevar" lambdas a un nivel superior (principalmente al nivel superior).

Doug Currie describe por qué querrías hacer esto.

Aquí hay un código de ejemplo (en JavaScript) de cómo se puede hacer esto manualmente:

function addFive(nr) 
{ 
    var x = 5; 
    function addX(y) 
    { 
    return x + y; 
    } 

    return addX(nr); 
} 

Ahora bien, si usted no desea esta función addX dentro de la definición de addFive se podía `levantar' a la nivel superior, así:

function addX(y) 
{ 
    return x + y; 
} 

function addFive(nr) 
{ 
    var x = 5; 

    return addX(nr); 
} 

sin embargo, esto no funcionará, ya que la variable x ya no está disponible en el contexto de la función addX. La manera de solucionar este problema es añadir un parámetro formal adicional para la función:

function addX(y, x) 
{ 
    return x + y; 
} 

function addFive(nr) 
{ 
    var x = 5; 

    return addX(nr, x); 
} 

Adición: Aquí hay un ejemplo muy artificial de un lambda 'escapar'. Donde no podrás hacer el levantamiento lambda tan fácilmente como he descrito.

function getAddFiveFunc() 
{ 
    var x = 5; 
    function addX(y) 
    { 
    return x + y; 
    } 

    return addX; 
} 

Ahora bien, si alguien llama a la función getAddFiveFunc, obtendrán una función de la espalda. Esta función podría usarse en todo tipo de lugares. Ahora, si desea levantar la función addX, tendrá que actualizar todas esas listas de llamadas.

+0

ejemplo muy simple, lo entiendo ahora :) gracias. por lo que se considera que una variable libre es una que se "extrae" del ambiente léxico circundante del cierre? aaaah, ¿entonces la forma en que el levantamiento acelera las cosas es que ya no tienes que llevar el entorno léxico para cada cierre? – deepblue

+0

Sólo iba a preguntarle qué sucede cuando se devuelve el cierre de su función de contenedor. Así que, si todavía quiero levantarlo, ¿cómo expondré los valores locales de la función del contenedor a las llamadas externas del cierre (en para pasarlos como parámetros)? – deepblue

+0

Si su compilador tiene acceso al programa completo en tiempo de compilación, podrá actualizar todos los sitios de llamadas. Sin embargo, la mayoría de los compiladores permiten la compilación por separado, donde cada módulo se compila por separado. En ese caso, si la lambda cruza los límites del módulo, el levantamiento lambda no será posible. –

1

Advertencia: Mi respuesta en realidad describe las variables capturadas, que es diferente de la elevación lambda. Lea mal la pregunta (necesita dormir). Pero dediqué un poco de tiempo a escribir esto, así que no me gusta borrarlo. Lo dejé como una comunidad WIKI.

El levantamiento lambda, a menudo referido como cierres, es una forma de permitir el acceso de variables de ámbito dentro de una expresión lambda anidada.

Es difícil entrar en los detalles esenciales de los cierres sin elegir un idioma en particular. Uno de los efectos secundarios del levantamiento de lambda, en cualquier lengua, es que tiende a extender la vida útil de una variable desde un ámbito local de corta duración a un alcance mucho más prolongado. Por lo general, esto ocurre en forma de transferir una variable de la pila al montón dentro del compilador. Esta es una acción muy específica del lenguaje y por lo tanto produce implementaciones muy diferentes basadas en el lenguaje.

Me centraré en C# ya que ese es probablemente el lenguaje más común para los lectores de desbordamiento de pila. Comencemos con el siguiente código.

public Func<int> GetAFunction() { 
    var x = 42; 
    Func<int> lambda1 =() => x; 
    Func<int> lambda2 =() => 42; 
    ... 
    return lambda1; 
} 

En este ejemplo, hemos creado 2 expresiones lambda. En ambos casos, se asigna a una instancia de delegado de tipo Func. Todos los delegados en .Net requieren que una función real los respalde en alguna parte. Entonces, bajo el capó, todas las expresiones lambda/funciones anónimas en C# se traducen en una definición de método.

Generar una función para lambda2 es bastante sencillo. Es una función aislada que simplemente devuelve un valor constante.

public static int RealLambda2() { 
    return 42; 
} 

Generar lambda1 es bastante más difícil. Una definición literal sería tener el siguiente

public static int RealLambda1() { 
    return x; 
} 

Esto, obviamente, no se compilará porque x no es accesible. Para que esto funcione, el compilador C# debe levantar la variable x en un cierre. A continuación, puede devolver un puntero a una función dentro del cierre para satisfacer la expresión delegado

class Closure1 { 
    int x; 
    public int RealLambda1() { 
    return x; 
    } 
} 

Este es un ejemplo bastante simple, pero todo funciona correctamente detalle el arte de la elevación. El diablo lamentablemente está en los detalles y se vuelve mucho más complejo con el escenario.

+1

Está hablando de variables capturadas, no de elevación lambda. – leppie

+0

@leppie, tiene razón, necesito dormir un poco – JaredPar

+0

La definición que escribió es ampliamente utilizada por Microsoft. Ver, por ejemplo, . –