stringtranslate.com

Codificación de la iglesia

En matemáticas , la codificación Church es un medio para representar datos y operadores en el cálculo lambda . Los numerales Church son una representación de los números naturales mediante la notación lambda. El método recibe su nombre de Alonzo Church , quien fue el primero en codificar datos en el cálculo lambda de esta manera.

Los términos que normalmente se consideran primitivos en otras notaciones (como números enteros, booleanos, pares, listas y uniones etiquetadas) se asignan a funciones de orden superior bajo la codificación Church. La tesis de Church-Turing afirma que cualquier operador computable (y sus operandos) se puede representar bajo la codificación Church. [ dudosodiscutir ] En el cálculo lambda no tipificado, el único tipo de datos primitivo es la función.

Usar

Una implementación sencilla de la codificación Church ralentiza algunas operaciones de acceso de a , donde es el tamaño de la estructura de datos, lo que hace que la codificación Church sea poco práctica. [1] La investigación ha demostrado que esto se puede abordar mediante optimizaciones específicas, pero la mayoría de los lenguajes de programación funcional , en cambio, expanden sus representaciones intermedias para contener tipos de datos algebraicos . [2] No obstante, la codificación Church se utiliza a menudo en argumentos teóricos, ya que es una representación natural para la evaluación parcial y la demostración de teoremas. [1] Las operaciones se pueden tipificar utilizando tipos de rango superior , [3] y la recursión primitiva es fácilmente accesible. [1] La suposición de que las funciones son los únicos tipos de datos primitivos agiliza muchas pruebas.

La codificación de Church está completa, pero solo representativamente. Se necesitan funciones adicionales para traducir la representación a tipos de datos comunes, para mostrarlos a las personas. En general, no es posible decidir si dos funciones son extensionalmente iguales debido a la indecidibilidad de la equivalencia del teorema de Church . La traducción puede aplicar la función de alguna manera para recuperar el valor que representa, o buscar su valor como un término lambda literal. El cálculo lambda generalmente se interpreta como el uso de la igualdad intensional . Existen problemas potenciales con la interpretación de los resultados debido a la diferencia entre la definición intensional y extensional de igualdad.

Números de la iglesia

Los numerales de la Iglesia son las representaciones de los números naturales según la codificación de la Iglesia. La función de orden superior que representa el número natural n es una función que asigna cualquier función a su composición de n veces . En términos más simples, el "valor" del numeral es equivalente a la cantidad de veces que la función encapsula su argumento.

Todos los numerales de Church son funciones que toman dos parámetros. Los numerales de Church 0 , 1 , 2 , ..., se definen de la siguiente manera en el cálculo lambda .

Comenzando con 0 sin aplicar la función en absoluto, proceda con 1 aplicando la función una vez, 2 aplicando la función dos veces, 3 aplicando la función tres veces, etc .:

El numeral 3 de la Iglesia representa la acción de aplicar cualquier función dada tres veces a un valor. La función suministrada se aplica primero a un parámetro suministrado y luego sucesivamente a su propio resultado. El resultado final no es el numeral 3 (a menos que el parámetro suministrado sea 0 y la función sea una función sucesora ). La función en sí misma, y ​​no su resultado final, es el numeral 3 de la Iglesia . El numeral 3 de la Iglesia significa simplemente hacer algo tres veces. Es una demostración ostensible de lo que significa "tres veces".

Cálculo con numerales de la Iglesia

Las operaciones aritméticas con números se pueden representar mediante funciones en numerales de Church. Estas funciones se pueden definir en el cálculo lambda o implementar en la mayoría de los lenguajes de programación funcional (consulte conversión de expresiones lambda a funciones ).

La función de suma utiliza la identidad .

La función sucesora es β-equivalente a .

La función de multiplicación utiliza la identidad .

La función exponencial viene dada por la definición de numerales de Church, . En la definición, sustituya para obtener y,

que da la expresión lambda,

La función es más difícil de entender.

Un numeral de Church aplica una función n veces. La función predecesora debe devolver una función que aplique su parámetro n - 1 veces. Esto se logra construyendo un contenedor alrededor de f y x , que se inicializa de una manera que omite la aplicación de la función la primera vez. Consulte predecesor para obtener una explicación más detallada.

La función resta se puede escribir basándose en la función predecesora.

Tabla de funciones de los numerales de la Iglesia

Notas :

  1. ^ Esta fórmula es la definición de un numeral de la Iglesia n con .
  2. ^ ab En la codificación de la Iglesia,

Derivación de la función predecesora

La función predecesora utilizada en la codificación de la Iglesia es,

.

Necesitamos una forma de aplicar la función 1 veces menos para construir la predecesora. Un numeral n aplica la función f n veces a x . La función predecesora debe usar el numeral n para aplicar la función n -1 veces.

Antes de implementar la función predecesora, aquí hay un esquema que envuelve el valor en una función contenedora. Definiremos nuevas funciones para usar en lugar de f y x , llamadas inc e init . La función contenedora se llama value . El lado izquierdo de la tabla muestra un numeral n aplicado a inc e init .

La regla general de recurrencia es:

Si también hay una función para recuperar el valor del contenedor (llamada extract ),

Luego, el extracto se puede utilizar para definir la función samenum como,

La función samenum no es intrínsecamente útil. Sin embargo, como inc delega la llamada de f a su argumento contenedor, podemos disponer que en la primera aplicación inc reciba un contenedor especial que ignore su argumento, lo que permite omitir la primera aplicación de f . Llamemos a este nuevo contenedor inicial const . El lado derecho de la tabla anterior muestra las expansiones de n inc const . Luego, al reemplazar init con const en la expresión para la misma función, obtenemos la función predecesora,

Como se explica a continuación, las funciones inc , init , const , value y extract pueden definirse como:

Lo que da la expresión lambda para pred como,

Otra forma de definir pred

Pred también puede definirse utilizando pares:

Esta es una definición más simple pero conduce a una expresión más compleja para pred. La expansión para :

División

La división de números naturales se puede implementar mediante [4]

El cálculo requiere muchas reducciones beta. A menos que se haga la reducción a mano, esto no importa tanto, pero es preferible no tener que hacer este cálculo dos veces. El predicado más simple para probar números es IsZero , así que considere la condición.

Pero esta condición es equivalente a , no a . Si se utiliza esta expresión, entonces la definición matemática de división dada anteriormente se traduce en función de los números de Church como,

Como se deseaba, esta definición tiene una única llamada a . Sin embargo, el resultado es que esta fórmula da el valor de .

Este problema se puede corregir sumando 1 a n antes de llamar a divide . La definición de divide es entonces:

divide1 es una definición recursiva. El combinador Y se puede utilizar para implementar la recursión. Crea una nueva función llamada div mediante:

Llegar,

Entonces,

dónde,

Da,

O como texto, usando \ para λ ,

dividir = (\n.((\f.(\xx x) (\xf (xx))) (\c.\n.\m.\f.\x.(\d.(\nn (\x .(\a.\bb)) (\a.\ba)) d ((\f.\xx) fx) (f (cdmfx))) ((\m.\nn (\n.\f.\ xn (\g.\hh (gf)) (\ux) (\uu)) m) nm))) ((\n.\f.\x. f (nfx)) n))

Por ejemplo, 9/3 está representado por

dividir (\f.\xf (f (f (f (f (f (f (f (f (f (f (f (f (fx))))))))) (\f.\xf (f (fx)))

Usando una calculadora de cálculo lambda, la expresión anterior se reduce a 3, usando el orden normal.

\f.\xf (f (f (x)))

Números con signo

Un enfoque simple para extender los numerales de la Iglesia a números con signo es utilizar un par de numerales de la Iglesia, que contiene numerales de la Iglesia que representan un valor positivo y uno negativo. [5] El valor entero es la diferencia entre los dos numerales de la Iglesia.

Un número natural se convierte en un número con signo mediante,

La negación se realiza intercambiando los valores.

El valor entero se representa de forma más natural si uno de los pares es cero. La función OneZero cumple esta condición.

La recursión se puede implementar utilizando el combinador Y,

Más y menos

La adición se define matemáticamente en el par por,

La última expresión se traduce al cálculo lambda como,

De manera similar se define la resta,

donación,

Multiplicar y dividir

La multiplicación puede definirse por:

La última expresión se traduce al cálculo lambda como,

Aquí se da una definición similar para la división, excepto que en esta definición, un valor en cada par debe ser cero (ver OneZero arriba). La función divZ nos permite ignorar el valor que tiene un componente cero.

Luego, divZ se utiliza en la siguiente fórmula, que es la misma que para la multiplicación, pero con mult reemplazado por divZ .

Números racionales y reales

Los números reales racionales y computables también pueden codificarse en el cálculo lambda. Los números racionales pueden codificarse como un par de números con signo. Los números reales computables pueden codificarse mediante un proceso de limitación que garantiza que la diferencia con el valor real difiere en un número que puede hacerse tan pequeño como sea necesario. [6] [7] Las referencias proporcionadas describen software que, en teoría, podría traducirse al cálculo lambda. Una vez que se definen los números reales, los números complejos se codifican naturalmente como un par de números reales.

Los tipos de datos y funciones descritos anteriormente demuestran que cualquier tipo de datos o cálculo puede codificarse en el cálculo lambda. Esta es la tesis de Church-Turing .

Traducción con otras representaciones

La mayoría de los lenguajes del mundo real admiten números enteros nativos de máquina; las funciones church y unchurch convierten entre números enteros no negativos y sus numerales Church correspondientes. Las funciones se dan aquí en Haskell , donde \corresponde a λ del cálculo Lambda. Las implementaciones en otros lenguajes son similares.

tipo Iglesia a = ( a -> a ) -> a -> a          iglesia :: Entero -> Iglesia Entero iglesia 0 = \ f -> \ x -> x iglesia n = \ f -> \ x -> f ( iglesia ( n - 1 ) f x )                       unchurch :: Iglesia Entero -> Entero unchurch cn = cn ( + 1 ) 0           

Booleanos de la iglesia

Los booleanos de Church son la codificación de Church de los valores booleanos verdadero y falso. Algunos lenguajes de programación los utilizan como modelo de implementación para la aritmética booleana; algunos ejemplos son Smalltalk y Pico .

La lógica booleana puede considerarse una opción. La codificación de la Iglesia de verdadero y falso son funciones de dos parámetros:

Las dos definiciones se conocen como Booleanos de la Iglesia:

Esta definición permite que los predicados (es decir, las funciones que devuelven valores lógicos ) actúen directamente como cláusulas if. Una función que devuelve un valor booleano, que luego se aplica a dos parámetros, devuelve el primero o el segundo parámetro:

se evalúa como cláusula-then si el predicado-x se evalúa como verdadero , y como cláusula-else si el predicado-x se evalúa como falso .

Dado que true y false eligen el primer o segundo parámetro, se pueden combinar para proporcionar operadores lógicos. Tenga en cuenta que existen múltiples implementaciones posibles de not .

Algunos ejemplos:

Predicados

Un predicado es una función que devuelve un valor booleano. El predicado más fundamental es , que devuelve si su argumento es el numeral de Church y si su argumento es cualquier otro numeral de Church:

El siguiente predicado prueba si el primer argumento es menor o igual que el segundo:

,

Por la identidad,

La prueba de igualdad puede implementarse como,

Pares de iglesias

Los pares de Church son la codificación de Church del tipo de par (dos tuplas). El par se representa como una función que toma un argumento de función. Cuando se le da su argumento, aplicará el argumento a los dos componentes del par. La definición en cálculo lambda es:

Por ejemplo,

Codificaciones de listas

Una lista ( inmutable ) se construye a partir de nodos de lista. Las operaciones básicas en la lista son:

A continuación presentamos cuatro representaciones diferentes de listas:

Dos pares como un nodo de lista

Una lista no vacía puede ser implementada por un par de Iglesias;

Sin embargo, esto no da una representación de la lista vacía, porque no hay un puntero "null". Para representar el valor nulo, el par puede estar envuelto en otro par, lo que da tres valores:

Utilizando esta idea las operaciones básicas de lista se pueden definir así: [8]

En un nodo nulo nunca se accede al segundo , siempre que la cabeza y la cola solo se apliquen a listas no vacías.

Un par como nodo de lista

Alternativamente, defina [9]

donde la última definición es un caso especial de la general

Representar la lista utilizandopliegue derecho

Como alternativa a la codificación mediante pares de Church, una lista se puede codificar identificándola con su función de plegado a la derecha . Por ejemplo, una lista de tres elementos x, y y z se puede codificar mediante una función de orden superior que, cuando se aplica a un combinador c y un valor n, devuelve cx (cy (czn)).

A esta representación de lista se le puede dar el tipo en el Sistema F.

Representar la lista utilizando la codificación Scott

Una representación alternativa es la codificación Scott, que utiliza la idea de continuaciones y puede conducir a un código más simple. [10] (véase también codificación Mogensen-Scott ).

En este enfoque, utilizamos el hecho de que las listas se pueden observar mediante expresiones de coincidencia de patrones. Por ejemplo, utilizando la notación Scala , si listdenota un valor de tipo Listcon lista vacía Nily constructor, Cons(h, t)podemos inspeccionar la lista y calcular nilCodeen caso de que la lista esté vacía y consCode(h, t)cuando no lo esté:

lista coincidente { caso Nil => nilCode caso Cons ( h , t ) => consCode ( h , t ) }           

La función listse determina por su acción sobre nilCodey consCode. Por lo tanto, definimos una lista como una función que acepta tales nilCodey consCodecomo argumentos, de modo que en lugar de la coincidencia de patrones anterior, podemos simplemente escribir:

Denotemos por nel parámetro correspondiente a nilCodey por cel parámetro correspondiente a consCode. La lista vacía es la que devuelve el argumento nulo:

La lista no vacía con cabeza hy cola tviene dada por

En términos más generales, un tipo de datos algebraicos con alternativas se convierte en una función con parámetros. Cuando el constructor tiene argumentos, el parámetro correspondiente de la codificación también recibe argumentos.

La codificación Scott se puede realizar en el cálculo lambda sin tipo, mientras que su uso con tipos requiere un sistema de tipos con recursión y polimorfismo de tipos. Una lista con el tipo de elemento E en esta representación que se utiliza para calcular valores de tipo C tendría la siguiente definición de tipo recursiva, donde '=>' denota el tipo de función:

tipo Lista = C => // argumento nulo ( E => Lista => C ) => // argumento cons C // resultado de la coincidencia de patrones               

Una lista que se puede utilizar para calcular tipos arbitrarios tendría un tipo que cuantifica sobre C. Una lista genérica [ aclaración necesaria ] en Etambién tomaría Ecomo argumento de tipo .

Véase también

Referencias

  1. ^ abc Trancón y Widemann, Baltasar; Parnas, David Lorge (2008). "Expresiones tabulares y programación funcional total". En Olaf Chitil; Zoltán Horváth; Viktória Zsók (eds.). Implementación y aplicación de lenguajes funcionales. 19.° taller internacional, IFL 2007, Friburgo, Alemania, 27-29 de septiembre de 2007. Documentos revisados ​​seleccionados. Lecture Notes in Computer Science. Vol. 5083. pp. 228-229. doi :10.1007/978-3-540-85373-2_13. ISBN 978-3-540-85372-5.
  2. ^ Jansen, Jan Martin; Koopman, Pieter WM; Plasmeijer, Marinus J. (2006). "Interpretación eficiente mediante la transformación de tipos de datos y patrones en funciones". En Nilsson, Henrik (ed.). Tendencias en programación funcional. Volumen 7. Bristol: Intellect. págs. 73–90. CiteSeerX 10.1.1.73.9841 . ISBN.  978-1-84150-188-8.
  3. ^ "Los predecesores y las listas no son representables en el cálculo lambda de tipos simples". Cálculo lambda y calculadoras lambda . okmij.org.
  4. ^ Allison, Lloyd. "Cálculo Lambda: números enteros".
  5. ^ Bauer, Andrej. "Respuesta de Andrej a una pregunta; "Representación de números negativos y complejos mediante cálculo lambda"".
  6. ^ "Aritmética real exacta". Haskell . Archivado desde el original el 26 de marzo de 2015.
  7. ^ Bauer, Andrej (26 de septiembre de 2022). «Software computacional de números reales». GitHub .
  8. ^ Pierce, Benjamin C. (2002). Tipos y lenguajes de programación . MIT Press . pág. 500. ISBN. 978-0-262-16209-8.
  9. ^ Tromp, John (2007). "14. Cálculo lambda binario y lógica combinatoria". En Calude, Cristian S (ed.). Aleatoriedad y complejidad, de Leibniz a Chaitin . World Scientific. págs. 237–262. ISBN 978-981-4474-39-9.
    Como PDF: Tromp, John (14 de mayo de 2014). "Cálculo lambda binario y lógica combinatoria" (PDF) . Consultado el 24 de noviembre de 2017 .
  10. ^ Jansen, Jan Martin (2013). "Programación en el cálculo λ: de Church a Scott y de regreso". En Achten, Peter; Koopman, Pieter WM (eds.). La belleza del código funcional: ensayos dedicados a Rinus Plasmeijer con motivo de su 61.º cumpleaños . Apuntes de clase en informática. Vol. 8106. Springer. págs. 168–180. doi :10.1007/978-3-642-40355-2_12.