stringtranslate.com

Elevación de lambda

El levantamiento de lambda es un metaproceso que reestructura un programa informático de modo que las funciones se definan de forma independiente entre sí en un ámbito global . Un "levantamiento" individual transforma una función local en una función global. Es un proceso de dos pasos, que consiste en:

El término "elevación de lambda" fue introducido por primera vez por Thomas Johnsson alrededor de 1982 y se consideró históricamente como un mecanismo para implementar lenguajes de programación funcional . Se utiliza junto con otras técnicas en algunos compiladores modernos .

La elevación de lambda no es lo mismo que la conversión de cierre. Requiere que se ajusten todos los sitios de llamada (agregando argumentos adicionales a las llamadas) y no introduce un cierre para la expresión lambda elevada. Por el contrario, la conversión de cierre no requiere que se ajusten los sitios de llamada, pero sí introduce un cierre para la expresión lambda que asigna variables libres a valores.

La técnica puede utilizarse en funciones individuales, en la refactorización de código , para hacer que una función sea utilizable fuera del ámbito en el que fue escrita. Los levantamientos lambda también pueden repetirse, para transformar el programa. Los levantamientos repetidos pueden usarse para convertir un programa escrito en cálculo lambda en un conjunto de funciones recursivas , sin lambdas. Esto demuestra la equivalencia de los programas escritos en cálculo lambda y los programas escritos como funciones. [1] Sin embargo, no demuestra la solidez del cálculo lambda para la deducción, ya que la reducción eta utilizada en el levantamiento lambda es el paso que introduce problemas de cardinalidad en el cálculo lambda, porque elimina el valor de la variable, sin verificar primero que solo hay un valor que satisface las condiciones en la variable (ver la paradoja de Curry ).

La elevación de lambda es costosa en términos de tiempo de procesamiento para el compilador. Una implementación eficiente de la elevación de lambda es costosa en términos de tiempo de procesamiento para el compilador. [2]

En el cálculo lambda sin tipo , donde los tipos básicos son funciones, la elevación puede cambiar el resultado de la reducción beta de una expresión lambda. Las funciones resultantes tendrán el mismo significado, en un sentido matemático, pero no se consideran la misma función en el cálculo lambda sin tipo. Véase también igualdad intensional versus igualdad extensional .

La operación inversa a la elevación de lambda es la caída de lambda. [3]

La eliminación de lambda puede hacer que la compilación de programas sea más rápida para el compilador y también puede aumentar la eficiencia del programa resultante, al reducir la cantidad de parámetros y el tamaño de los marcos de pila. Sin embargo, hace que sea más difícil reutilizar una función. Una función eliminada está vinculada a su contexto y solo se puede usar en un contexto diferente si se elimina primero.

Algoritmo

El siguiente algoritmo es una forma de aplicar un efecto lambda a un programa arbitrario en un lenguaje que no admite cierres como objetos de primera clase :

  1. Cambie el nombre de las funciones para que cada una tenga un nombre único.
  2. Reemplace cada variable libre con un argumento adicional a la función que la contiene y pase ese argumento a cada uso de la función.
  3. Reemplace cada definición de función local que no tenga variables libres con una función global idéntica.
  4. Repita los pasos 2 y 3 hasta eliminar todas las variables libres y funciones locales.

Si el lenguaje tiene cierres como objetos de primera clase que pueden pasarse como argumentos o devolverse desde otras funciones, el cierre deberá estar representado por una estructura de datos que capture los enlaces de las variables libres.

Ejemplo

El siguiente programa OCaml calcula la suma de los números enteros del 1 al 100:

sea  ​​rec  suma  n  =  si  n  =  1  entonces  1  de lo contrario  sea  f  x  =  n  +  x  en  f  ( suma  ( n  -  1 ))  en suma  100

(La función f let recse declara sumcomo una función que puede llamarse a sí misma). La función f, que suma el argumento de suma a la suma de los números menores que el argumento, es una función local. Dentro de la definición de f, n es una variable libre. Comience convirtiendo la variable libre en un parámetro:

sea  ​​rec  suma  n  =  si  n  =  1  entonces  1  de lo contrario  sea  f  w  x  =  w  +  x  en  f  n  ( suma  ( n  -  1 ))  en suma  100

A continuación, eleve f a una función global:

sea  ​​rec  f  w  x  =  w  +  x y  suma  n  =  si  n  =  1  entonces  1  de lo contrario  f  n  ( suma  ( n  -  1 ))  en suma  100

El siguiente es el mismo ejemplo, esta vez escrito en JavaScript :

// Versión inicialfunción suma ( n ) { función f ( x ) { devolver n + x ; }           si ( n == 1 ) devuelve 1 ; de lo contrario devuelve f ( suma ( n - 1 )); }          // Después de convertir la variable libre n en un parámetro formal wfunción suma ( n ) { función f ( w , x ) { devolver w + x ; }            si ( n == 1 ) devuelve 1 ; de lo contrario devuelve f ( n , suma ( n - 1 )); }           // Después de elevar la función f al ámbito globalfunción f ( w , x ) { devuelve w + x ; }       función suma ( n ) { si ( n == 1 ) devuelve 1 ; de lo contrario devuelve f ( n , suma ( n - 1 )); }              

Elevación de lambda versus cierres

La elevación y el cierre de Lambda son métodos para implementar programas estructurados en bloques . Implementa la estructura en bloques eliminándola. Todas las funciones se elevan al nivel global. La conversión de cierre proporciona un "cierre" que vincula el marco actual con otros marcos. La conversión de cierre requiere menos tiempo de compilación.

Las funciones recursivas y los programas estructurados en bloques, con o sin elevación, pueden implementarse utilizando una implementación basada en pila , que es simple y eficiente. Sin embargo, una implementación basada en un marco de pila debe ser estricta (ansiosa) . La implementación basada en un marco de pila requiere que la vida de las funciones sea del tipo último en entrar, primero en salir (LIFO). Es decir, la función más reciente en comenzar su cálculo debe ser la primera en finalizar.

Algunos lenguajes funcionales (como Haskell ) se implementan utilizando la evaluación diferida , que retrasa el cálculo hasta que se necesita el valor. La estrategia de implementación diferida le da flexibilidad al programador. La evaluación diferida requiere retrasar la llamada a una función hasta que se realiza una solicitud al valor calculado por la función. Una implementación es registrar una referencia a un "marco" de datos que describe el cálculo, en lugar del valor. Más tarde, cuando se requiere el valor, el marco se utiliza para calcular el valor, justo a tiempo para cuando se necesita. El valor calculado luego reemplaza la referencia.

El "marco" es similar a un marco de pila , con la diferencia de que no se almacena en la pila. La evaluación diferida requiere que todos los datos necesarios para el cálculo se guarden en el marco. Si la función se "elimina", entonces el marco solo necesita registrar el puntero de función y los parámetros de la función. Algunos lenguajes modernos utilizan la recolección de basura en lugar de la asignación basada en pila para administrar la vida de las variables. En un entorno administrado y recolectado de basura, un cierre registra referencias a los marcos de los cuales se pueden obtener valores. En contraste, una función eliminada tiene parámetros para cada valor necesario en el cálculo.

Expresiones let y cálculo lambda

La expresión Let es útil para describir el levantamiento y la caída, y para describir la relación entre ecuaciones recursivas y expresiones lambda. La mayoría de los lenguajes funcionales tienen expresiones let. Además, los lenguajes de programación estructurados en bloques como ALGOL y Pascal son similares en el sentido de que también permiten la definición local de una función para su uso en un ámbito restringido .

La expresión let utilizada aquí es una versión totalmente recursiva de let rec , tal como se implementa en muchos lenguajes funcionales.

Las expresiones let están relacionadas con el cálculo lambda . El cálculo lambda tiene una sintaxis y una semántica simples, y es bueno para describir la elevación de lambda. Es conveniente describir la elevación de lambda como traducciones de lambda a una expresión let , y la eliminación de lambda como lo inverso. Esto se debe a que las expresiones let permiten la recursión mutua, que es, en cierto sentido, más elevada que la que admite el cálculo lambda. El cálculo lambda no admite la recursión mutua y solo se puede definir una función en el ámbito global más externo.

Las reglas de conversión que describen la traducción sin elevación se proporcionan en el artículo Expresión Let .

Las siguientes reglas describen la equivalencia de las expresiones lambda y let,

Se darán metafunciones que describen la elevación y la caída de lambda. Una metafunción es una función que toma un programa como parámetro. El programa son datos para el metaprograma. El programa y el metaprograma se encuentran en diferentes metaniveles.

Se utilizarán las siguientes convenciones para distinguir el programa del metaprograma,

Para simplificar, se aplicará la primera regla que establece que se aplicarán las coincidencias. Las reglas también suponen que las expresiones lambda se han procesado previamente de modo que cada abstracción lambda tenga un nombre único.

El operador de sustitución se utiliza ampliamente. La expresión significa sustituir cada ocurrencia de G en L por S y devolver la expresión. La definición utilizada se extiende para cubrir la sustitución de expresiones, a partir de la definición proporcionada en la página de cálculo Lambda . La comparación de expresiones debe comparar expresiones para determinar la equivalencia alfa (cambio de nombre de las variables).

Elevación de lambda en el cálculo lambda

Cada elevación de lambda toma una abstracción de lambda que es una subexpresión de una expresión de lambda y la reemplaza por una llamada de función (aplicación) a una función que crea. Las variables libres en la subexpresión son los parámetros de la llamada de función.

Los aumentos de lambda se pueden utilizar en funciones individuales, en la refactorización de código , para hacer que una función sea utilizable fuera del ámbito en el que fue escrita. Dichos aumentos también se pueden repetir, hasta que la expresión no tenga abstracciones de lambda, para transformar el programa.

Elevación lambda

Se le asigna una subexpresión dentro de una expresión a un elevador para elevarlo hasta la parte superior de esa expresión. La expresión puede ser parte de un programa más grande. Esto permite controlar a dónde se eleva la subexpresión. La operación de elevación lambda que se utiliza para realizar un elevador dentro de un programa es:

La subexpresión puede ser una abstracción lambda o una abstracción lambda aplicada a un parámetro.

Son posibles dos tipos de elevación.

Una función anónima tiene una expresión de elevación que es solo una abstracción lambda. Se considera que define una función anónima. Se debe crear un nombre para la función.

Una expresión de elevación con nombre tiene una abstracción lambda aplicada a una expresión. Esta elevación se considera una definición con nombre de una función.

Ascensor anónimo

Un ascensor anónimo toma una abstracción lambda (llamada S ). Para S ;

La elevación lambda es la sustitución de la abstracción lambda S por una aplicación de función, junto con la adición de una definición para la función.

La nueva expresión lambda tiene S sustituida por G. Nótese que L [ S := G ] significa sustitución de S por G en L . En las definiciones de funciones se agregó la definición de función G = S.

En la regla anterior, G es la función de aplicación que sustituye a la expresión S. Se define por:

donde V es el nombre de la función. Debe ser una variable nueva, es decir, un nombre que no se haya utilizado ya en la expresión lambda.

donde es una metafunción que devuelve el conjunto de variables utilizadas en E.

Construyendo la llamada

La llamada de función G se construye agregando parámetros para cada variable en el conjunto de variables libres (representado por V ), a la función H ,

Ascensor con nombre

El ascensor nombrado es similar al ascensor anónimo excepto que se proporciona el nombre de función V.

En cuanto al ascensor anónimo, la expresión G se construye a partir de V aplicando las variables libres de S. Se define por,

Transformación lambda-lift

Una transformación de elevación lambda toma una expresión lambda y eleva todas las abstracciones lambda a la parte superior de la expresión. Las abstracciones se traducen luego en funciones recursivas , lo que elimina las abstracciones lambda. El resultado es un programa funcional en la forma

donde M es una serie de definiciones de funciones y N es la expresión que representa el valor devuelto.

Por ejemplo,

Luego, la función meta de-let se puede utilizar para convertir el resultado nuevamente en cálculo lambda.

El procesamiento de transformación de la expresión lambda es una serie de elevaciones. Cada elevación tiene,

Después de aplicar los elevadores, los lets se combinan para formar un solo let.

Luego, se aplica la eliminación de parámetros para eliminar los parámetros que no son necesarios en la expresión "let". La expresión let permite que las definiciones de funciones hagan referencia entre sí directamente, mientras que las abstracciones lambda son estrictamente jerárquicas y una función no puede hacer referencia directamente a sí misma.

Elegir la expresión para levantar

Hay dos formas diferentes de seleccionar una expresión para su levantamiento. La primera trata todas las abstracciones lambda como si definieran funciones anónimas. La segunda, trata las abstracciones lambda que se aplican a un parámetro como si definieran una función. Las abstracciones lambda aplicadas a un parámetro tienen una doble interpretación, ya sea como una expresión let que define una función o como una función anónima. Ambas interpretaciones son válidas.

Estos dos predicados son necesarios para ambas definiciones.

lambda-free : una expresión que no contiene abstracciones lambda.

lambda-anon : una función anónima. Una expresión como donde X es lambda libre.

Elegir funciones anónimas solo para elevación

Busque la abstracción anónima más profunda, de modo que cuando se aplique la elevación, la función elevada se convierta en una ecuación simple. Esta definición no reconoce una abstracción lambda con un parámetro como definitoria de una función. Se considera que todas las abstracciones lambda definen funciones anónimas.

lift-choice - El primer anónimo que se encuentra al recorrer la expresión o none si no hay ninguna función.

Por ejemplo,

Elección de funciones nombradas y anónimas para el levantamiento

Busque la definición de función anónima o nombrada más profunda, de modo que cuando se aplique la elevación, la función elevada se convierta en una ecuación simple. Esta definición reconoce una abstracción lambda con un parámetro real como la definición de una función. Solo las abstracciones lambda sin una aplicación se tratan como funciones anónimas.

con nombre lambda
Una función nombrada. Una expresión como donde M es lambda libre y N es lambda libre o una función anónima.
elección de ascensor
La primera función anónima o con nombre que se encuentra al recorrer la expresión o ninguna si no hay ninguna función.

Por ejemplo,

Ejemplos

Por ejemplo, el combinador Y ,

se levanta como,

y después de eliminar los parámetros,

Como expresión lambda (ver Conversión de expresiones let a lambda ),

Si solo se levantan funciones anónimas, el combinador Y es,

y después de eliminar los parámetros,

Como expresión lambda,

La primera subexpresión que se debe elegir para la elevación es . Esto transforma la expresión lambda en y crea la ecuación .

La segunda subexpresión que se debe elegir para la elevación es . Esto transforma la expresión lambda en y crea la ecuación .

Y el resultado es,

Sorprendentemente, este resultado es más simple que el que se obtiene al levantar funciones nombradas.

Ejecución

Aplicar función a K ,

Entonces,

o

El Y-Combinator llama a su parámetro (función) repetidamente sobre sí mismo. El valor se define si la función tiene un punto fijo . Pero la función nunca terminará.

Caída de lambda en el cálculo lambda

La eliminación de lambda [4] consiste en reducir el alcance de las funciones y utilizar el contexto del alcance reducido para reducir la cantidad de parámetros de las funciones. Al reducir la cantidad de parámetros, las funciones resultan más fáciles de comprender.

En la sección de elevación de Lambda, se describió una metafunción para primero elevar y luego convertir la expresión lambda resultante en una ecuación recursiva. La metafunción Lambda Drop realiza lo inverso convirtiendo primero las ecuaciones recursivas en abstracciones lambda y luego eliminando la expresión lambda resultante en el alcance más pequeño que cubre todas las referencias a la abstracción lambda.

La caída de lambda se realiza en dos pasos,

Caída de lambda

Se aplica una eliminación de Lambda a una expresión que forma parte de un programa. La eliminación se controla mediante un conjunto de expresiones de las que se excluirá la eliminación.

dónde,

L es la abstracción lambda que se debe descartar.
P es el programa
X es un conjunto de expresiones que deben excluirse de la eliminación.

Transformación de caída de lambda

La transformación lambda drop elimina todas las abstracciones de una expresión. El hundimiento se excluye de las expresiones de un conjunto de expresiones.

dónde,

L es la expresión a transformar.
X es un conjunto de subexpresiones que se excluirán de la eliminación.

hundir-tran hunde cada abstracción, empezando por lo más interno,

Abstracción hundiéndose

Hundir consiste en mover una abstracción lambda hacia adentro lo más lejos posible de modo que todavía esté fuera de todas las referencias a la variable.

Aplicación - 4 casos.

Abstracción . Utilice el cambio de nombre para garantizar que todos los nombres de las variables sean distintos.

Variable - 2 casos.

La prueba de hundimiento excluye expresiones de la eliminación.

Ejemplo

Eliminación de parámetros

La eliminación de parámetros consiste en optimizar una función para su posición en la función. La eliminación de Lambda agregó parámetros que eran necesarios para que una función pueda sacarse de su contexto. En la eliminación, este proceso se invierte y se pueden eliminar los parámetros adicionales que contienen variables que están libres.

Omitir un parámetro es quitar un parámetro innecesario de una función, donde el parámetro real que se pasa siempre es la misma expresión. Las variables libres de la expresión también deben ser libres donde se define la función. En este caso, el parámetro que se omite se reemplaza por la expresión en el cuerpo de la definición de la función. Esto hace que el parámetro sea innecesario.

Por ejemplo, considere,

En este ejemplo, el parámetro real del parámetro formal o es siempre p . Como p es una variable libre en toda la expresión, el parámetro puede omitirse. El parámetro real del parámetro formal y es siempre n . Sin embargo, n está ligado en una abstracción lambda. Por lo tanto, este parámetro no puede omitirse.

El resultado de eliminar el parámetro es:

Para el ejemplo principal,

La definición de drop-params-tran es,

dónde,

Crear listas de parámetros

Para cada abstracción que define una función, genere la información necesaria para tomar decisiones sobre la eliminación de nombres. Esta información describe cada parámetro: el nombre del parámetro, la expresión para el valor real y una indicación de que todas las expresiones tienen el mismo valor.

Por ejemplo, en,

Los parámetros de la función g son,

Cada abstracción se renombra con un nombre único y la lista de parámetros se asocia con el nombre de la abstracción. Por ejemplo, g tiene una lista de parámetros.

build-param-lists construye todas las listas de una expresión recorriendo la expresión. Tiene cuatro parámetros:

Abstracción : se analiza una expresión lambda de la forma para extraer los nombres de los parámetros de la función.

Localice el nombre y comience a crear la lista de parámetros para el nombre, completando los nombres de parámetros formales. Reciba también cualquier lista de parámetros real del cuerpo de la expresión y devuélvala como la lista de parámetros real de esta expresión

Variable : Una llamada a una función.

Para un nombre de función o parámetro, comience a completar la lista de parámetros real generando la lista de parámetros para este nombre.

Aplicación : se procesa una aplicación (llamada de función) para extraer detalles de los parámetros reales.

Recupere las listas de parámetros para la expresión y el parámetro. Recupere un registro de parámetro de la lista de parámetros de la expresión y verifique que el valor del parámetro actual coincida con este parámetro. Registre el valor del nombre del parámetro para usarlo más adelante en la verificación.

La lógica anterior es bastante sutil en su funcionamiento. El mismo indicador de valor nunca se establece en verdadero. Solo se establece en falso si no se pueden hacer coincidir todos los valores. El valor se recupera utilizando S para crear un conjunto de valores booleanos permitidos para S. Si true es un miembro, entonces todos los valores para este parámetro son iguales y el parámetro puede descartarse.

De manera similar, def utiliza la teoría de conjuntos para consultar si a una variable se le ha dado un valor;

Let - Expresión Let.

Y - Para usar en "dejar".

Ejemplos

Por ejemplo, crear las listas de parámetros para,

da,

y se elimina el parámetro o para dar,

Otro ejemplo es,

Aquí x es igual a f. La asignación de la lista de parámetros es,

y se elimina el parámetro x para dar,

Parámetros de caída

Utilice la información obtenida mediante las listas de parámetros de creación para eliminar los parámetros reales que ya no son necesarios. drop-params tiene los parámetros,

Abstracción

dónde,

dónde,

Variable

Para un nombre de función o parámetro, comience a completar la lista de parámetros real generando la lista de parámetros para este nombre.

Aplicación : se procesa una aplicación (llamada de función) para extraer

Let - Expresión Let.

Y - Para usar en "dejar".

Eliminar parámetros formales

drop-formal elimina los parámetros formales en función del contenido de las listas desplegables. Sus parámetros son:

El formalismo se define como,

Lo cual se puede explicar como,

  1. Si todos los parámetros reales tienen el mismo valor y todas las variables libres de ese valor están disponibles para la definición de la función, elimine el parámetro y reemplace el parámetro antiguo con su valor.
  2. De lo contrario, no elimine el parámetro.
  3. de lo contrario, devuelve el cuerpo de la función.

Ejemplo

Comenzando con la definición de la función del combinador Y,

Lo que devuelve el combinador Y ,

Véase también

Referencias

  1. ^ Johnsson, Thomas (1985). "Lambda Lifting: Transforming Programs to Recursive Equations". En Jouannaud, JP (ed.). Lenguajes de programación funcional y arquitectura informática. FPCA 1985. Lecture Notes in Computer Science. Vol. 201. Springer. CiteSeerX  10.1.1.48.4346 . doi :10.1007/3-540-15975-4_37. ISBN . 3-540-15975-4.
  2. ^ Morazán, Marco T.; Schultz, Ulrik P. (2008), "Optimal Lambda Lifting in Quadratic Time", Implementation and Application of Functional Languages ​​— Revised Selected Papers , págs. 37–56, doi :10.1007/978-3-540-85373-2_3, ISBN 978-3-540-85372-5
  3. ^ Danvy, O.; Schultz, UP (1997). "Lambda-dropping". Avisos SIGPLAN de la ACM . 32 (12): 90. doi : 10.1145/258994.259007 .
  4. ^ Danvy, Olivier; Schultz, Ulrik P. (octubre de 2000). "Lambda-Dropping: Transforming Recursive Equations into Programs with Block Structure" (PDF) . Ciencias de la computación teórica . 248 (1–2): 243–287. CiteSeerX 10.1.1.16.3943 . doi :10.1016/S0304-3975(00)00054-2. BRICS-RS-99-27. 

Enlaces externos