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 números de Church son una representación de los números naturales utilizando notación lambda. El método lleva el 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) puede representarse mediante la codificación de Church. [ dudoso ] En el cálculo lambda sin tipo, 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 desde hasta , donde está el tamaño de la estructura de datos, lo que hace que la codificación Church no sea práctica. [1] Las investigaciones han demostrado que esto se puede abordar mediante optimizaciones específicas, pero la mayoría de los lenguajes de programación funcionales expanden sus representaciones intermedias para contener tipos de datos algebraicos . [2] No obstante, la codificación de 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 escribir utilizando tipos de clasificación superior , [3] y la recursividad primitiva es fácilmente accesible. [1] La suposición de que las funciones son los únicos tipos de datos primitivos simplifica muchas pruebas.

La codificación de la iglesia es completa pero sólo representativa. 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 extensivamente 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 igualdad intensional . Existen problemas potenciales con la interpretación de los resultados debido a la diferencia entre la definición intencional y extensional de igualdad.

Números de la iglesia

Los números de la Iglesia son las representaciones de números naturales bajo 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 n veces . En términos más simples, el "valor" del numeral equivale al número de veces que la función encapsula su argumento.

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

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

El número 3 de la Iglesia representa la acción de aplicar cualquier función dada tres veces a un valor. La función proporcionada se aplica primero a un parámetro proporcionado y luego sucesivamente a su propio resultado. El resultado final no es el número 3 (a menos que el parámetro proporcionado sea 0 y la función sea una función sucesora ). La función en sí, y no su resultado final, es el número eclesiástico 3 . El número 3 de la Iglesia significa simplemente hacer cualquier cosa tres veces. Es una demostración ostensiva de lo que se entiende por "tres tiempos".

Cálculo con números de iglesia.

Las operaciones aritméticas con números pueden representarse mediante funciones con números de Iglesia. Estas funciones pueden definirse en cálculo lambda o implementarse en la mayoría de los lenguajes de programación funcionales (consulte convertir expresiones lambda en funciones ).

La función de suma usa la identidad .

La función sucesora es β-equivalente a .

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

La función de exponenciación viene dada por la definición de números 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 número de la Iglesia 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 manera que omite la aplicación de la función la primera vez. Consulte el predecesor para obtener una explicación más detallada.

La función de resta se puede escribir en función de la función predecesora.

Tabla de funciones en números de la iglesia.

información Nota:

  1. ^ Esta fórmula es la definición de un número 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,

.

Para construir el predecesor necesitamos una forma de aplicar la función 1 menos veces. Un numeral n aplica la función f n veces a x . La función predecesora debe usar el número 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 valor . El lado izquierdo de la tabla muestra un número 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 extraer ),

Luego, se puede utilizar extracto 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 arreglar que en la primera aplicación inc reciba un contenedor especial que ignore su argumento permitiendo omitir la primera aplicación de f . Llame a este nuevo contenedor inicial const . El lado derecho de la tabla anterior muestra las expansiones de n inc const . Luego, reemplazando 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 se pueden definir como,

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

Otra forma de definir pred

Pred también se puede definir usando 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 hagamos la reducción a mano, esto no importa mucho, 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 equivale a , no . Si se usa esta expresión, entonces la definición matemática de división dada anteriormente se traduce en función de los números de la Iglesia como,

Como se desee, 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 an antes de llamar a divide . La definición de división es entonces,

divide1 es una definición recursiva. El combinador Y se puede utilizar para implementar la recursividad. Crea una nueva función llamada div by;

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 (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 firmados

Un método sencillo para extender los números de la Iglesia a números con signo es utilizar un par de la Iglesia, que contenga números de la Iglesia que representen un valor positivo y uno negativo. [5] El valor entero es la diferencia entre los dos números 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 del par es cero. La función OneZero logra esta condición,

La recursividad se puede implementar usando el combinador Y,

Más y menos

La suma 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 componente cero.

Luego, divZ se usa 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 se pueden codificar en cálculo lambda. Los números racionales se pueden codificar como un par de números con signo. Los números reales computables pueden codificarse mediante un proceso limitante que garantiza que la diferencia con el valor real difiere en un número que puede hacerse tan pequeño como necesitemos. [6] [7] Las referencias proporcionadas describen software que, en teoría, podría traducirse al cálculo lambda. Una vez definidos 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 se puede codificar en cálculo lambda. Ésta 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 la máquina; las funciones church y unchurch convierten entre números enteros no negativos y sus correspondientes números Church. Las funciones se dan aquí en Haskell , donde \corresponde a λ del cálculo Lambda. Las implementaciones en otros idiomas son similares.

escriba 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 la Iglesia son la codificación de la Iglesia de los valores booleanos verdadero y falso. Algunos lenguajes de programación los utilizan como modelo de implementación para la aritmética booleana; ejemplos son Smalltalk y Pico .

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

Las dos definiciones se conocen como booleanos de la iglesia:

Esta definición permite que los predicados (es decir, 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 en la cláusula entonces si el predicado-x se evalúa como verdadero y en la cláusula más si el predicado-x se evalúa como falso .

Debido a que verdadero y falso eligen el primer o segundo parámetro, pueden combinarse 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 número de la Iglesia y si su argumento es cualquier otro número de la Iglesia:

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 iglesias son la codificación de iglesias del tipo par (dos tuplas). El par se representa como una función que toma un argumento de función. Cuando se le dé su argumento, lo aplicará a los dos componentes del par. La definición en el cálculo lambda es,

Por ejemplo,

Codificaciones de lista

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

A continuación damos cuatro representaciones diferentes de listas:

Dos pares como nodo de lista

Un par de Iglesias puede implementar una lista no vacía;

Sin embargo, esto no proporciona una representación de la lista vacía, porque no hay ningún puntero "nulo". Para representar nulo, el par puede estar envuelto en otro par, dando tres valores:

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

En un nodo nulo, nunca se accede al segundo , siempre que el principio y el final 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 del general

Representar la lista usando el pliegue derecho.

Como alternativa a la codificación que utiliza pares de Iglesias, se puede codificar una lista identificándola con su función de plegado derecho . 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 tipo en el Sistema F.

Representar la lista usando 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] (ver 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, usando la notación Scala , si listdenota un valor de tipo Listcon una lista Nily un constructor vacíos Cons(h, t), podemos inspeccionar la lista y calcular nilCodeen caso de que la lista esté vacía y consCode(h, t)cuando la lista no esté vacía:

lista de coincidencias { case Nil => nilCode case Cons ( h , t ) => consCode ( h , t ) }           

El listviene dado por cómo actúa sobre nilCodey consCode. Por lo tanto, definimos una lista como una función que acepta tales nilCodey consCodecomo argumentos, de modo que en lugar del patrón 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

De manera más general, un tipo de datos algebraico con alternativas se convierte en una función con parámetros. Cuando el ésimo constructor tiene argumentos, el parámetro correspondiente de la codificación también toma argumentos.

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

tipo Lista = C => // argumento nulo ( E => Lista => C ) => // argumento contras 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 ] también Etomaría Ecomo argumento de tipo.

Ver 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. XIX Taller Internacional, IFL 2007, Friburgo, Alemania, 27 al 29 de septiembre de 2007 Artículos seleccionados revisados. Apuntes de conferencias sobre informática. vol. 5083, págs. 228-229. doi :10.1007/978-3-540-85373-2_13. ISBN 978-3-540-85372-5.
  2. ^ Jansen, Jan Martín; Koopman, Pieter WM; Plasmeijer, Marinus J. (2006). "Interpretación eficiente transformando tipos y patrones de datos en funciones". En Nilsson, Henrik (ed.). Tendencias en programación funcional. Volumen 7 . Bristol: intelecto. págs. 73–90. CiteSeerX 10.1.1.73.9841 . ISBN  978-1-84150-188-8.
  3. ^ "El predecesor y las listas no se pueden representar en un cálculo lambda simplemente escrito". Cálculo Lambda y Calculadoras Lambda . okmij.org.
  4. ^ Allison, Lloyd. "Cálculo Lambda de 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". Haskel . 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, Benjamín C. (2002). Tipos y Lenguajes de Programación . Prensa del MIT . pag. 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 . Científico mundial. 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 Martín (2013). "Programación en λ-Cálculo: de Church a Scott y viceversa". 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 conferencias sobre informática. vol. 8106. Saltador. págs. 168–180. doi :10.1007/978-3-642-40355-2_12.