Representación de números negativos y complejos usando cálculo Lambda

Respuestas:

18

Primero codifique números y pares naturales, como lo describe jmad.

Representa un número entero k como un par de números naturales (a,b) tal que k=ab . Luego puede definir las operaciones habituales en enteros como (usando la notación de Haskell para cálculo λ ):

neg = \k -> (snd k, fst k)
add = \k m -> (fst k + fst m, snd k + snd m)
sub = \k m -> add k (neg m)
mul = \k m -> (fst k * fst m + snd k * snd m, fst k * snd m + snd k * fst m)

El caso de los números complejos es similar en el sentido de que un número complejo está codificado como un par de reales. Pero una pregunta más complicada es cómo codificar los reales. Aquí tienes que hacer más trabajo:

  1. Codifique un número racional como un par ( k , a ) donde k es un número entero, a es natural y q = k / ( 1 + a ) .q(k,a)kaq=k/(1+a)
  2. Codifique un número real mediante una función f tal que para cada k N natural , f k codifique un número racional q tal que | x - q | < 2 - k . En otras palabras, un real se codifica como una secuencia de racionales que convergen a la velocidad k 2 - k .xfkNfkq|xq|<2kk2k

Codificar reales es mucho trabajo y no quieres hacerlo en el cálculo . Pero vea, por ejemplo, el subdirectorio de Marshall para una implementación simple de reales en Haskell puro. En principio, esto podría traducirse al cálculo λ puro .λetc/haskellλ

Andrej Bauer
fuente
1
Wow =) Me pregunto intuitivamente qué significa eso ... por ejemplo, usando la codificación de los números de la iglesia ... es decir. un número de iglesia de valor entero n está representado por una función que aplica una función a un valor n veces. ¿Los pares y los valores lambda negativos tienen esa sensación intuitiva similar sobre ellos?
zcaudate
1
La codificación de la iglesia codifica los números naturales , 1 , 2 , ... No codifica los números negativos. En la respuesta anterior supuse que ya conoces una codificación de números naturales, así que te expliqué cómo obtener números enteros. Los enteros como los codifiqué son una construcción más formal, a diferencia de los números de la Iglesia, que están más intrincadamente conectados con el cálculo λ . No creo que "valores lambda negativos" sea una frase significativa. 012λ
Andrej Bauer
@zcaudate [anotaciones Tipo: i:ℤ, x:a, f,u,s:a→a, p:(a→a,a→a)] Si codifican ℤ como (Sign,ℕ)entonces, dado un par de funciones (s,f)como p, el término λi.λp.λx.(fst i) (fst p) id ((snd i) (snd p) x)producirá ya sea f(…f(x)…)o s(f(…f(x)…))(si el resultado es negativo). Si codifica ℤ como (ℕ,ℕ), necesita una función que tenga un inverso, dado un par (f,u)y x, la función λi.λp.λx.(snd i)(snd p)((fst i)(fst p) x)producirá u(…u(f(…f(x)…))…)lo que dejará los tiempos faplicados . Ambos funcionan en diferentes contextos (el resultado puede "voltearse" || es invertible). ixf
nadie
@zcaudate Las funciones adicionales son necesarias ya que los números codificados por la Iglesia "se repiten por sí mismos", pero los pares solo le entregarán sus componentes. Las funciones auxiliares solo pegan los componentes en el orden "correcto" (que ocurre automáticamente para los nats). Ver también: en.wikipedia.org/wiki/… - La codificación de la iglesia es básicamente fold . ctorpara cualquier constructor y ese tipo fold( r). (Es por eso que, para los tipos recursivos, los datos "recurrirán por sí mismos". Para los tipos no recursivos es más como una casecoincidencia de patrón /.)
nadie
13

El cálculo Lambda puede codificar la mayoría de las estructuras de datos y tipos básicos. Por ejemplo, puede codificar un par de términos existentes en el cálculo lambda, utilizando la misma codificación de Church que suele ver para codificar enteros no negativos y booleanos:

fst = λ p . p ( λ x y . x ) snd = λ p . p ( λ x y . y )

pair=λxyz.zxy
fst=λp.p(λxy.x)
snd=λp.p(λxy.y)

Entonces el par es p = ( emparejar  un b ) y si quieres volver una y b se puede hacer ( FST  p ) y ( SND  p ) .(a,b)p=(pair ab)ab(fst p)(snd p)

Eso significa que puede representar fácilmente enteros positivos y negativos con un par: el signo a la izquierda y el valor absoluto a la derecha. El signo es un booleano que especifica si el número es positivo. El derecho es un número natural que usa la codificación de Church.

(sign,n)

Y ahora que tienes enteros relativos. La multiplicación es fácil de definir, solo tiene que aplicar la función xor en el signo y la multiplicación en números naturales en el valor absoluto:

mult=λab.pair  (xor(fst a)(fst b))  (mult(snd a)(snd b))

Para definir la suma, debe comparar dos números naturales y usar la resta cuando los signos son diferentes, por lo que este no es un término λ pero puede adaptarlo si realmente desea:

add=λab.{(true,add(snd a)(snd b))if a0b0(false,add(snd a)(snd b))if a<0b<0(true,sub(snd a)(snd b))if a0b<0|a||b|(false,sub(snd b)(snd a))if a0b<0|a|<|b|(true,sub(snd b)(snd a))if a<0b0|a|<|b|(false,sub(snd a)(snd b))if a<0b0|a||b|

pero luego la resta es realmente fácil de definir:

minus=λa.pair(not(fst a))(snd a)
sub=λab.add(a)(minusb)

(a,b)a+bi

add[i]=λz1z2.pair(add(fst z1)(fst z2))(add(snd z1)(snd z2))
jmad
fuente
66
Puede evitar las distinciones entre mayúsculas y minúsculas si en cambio representa el número entero k como un par de números naturales (un,si) tal que k=un-si.
Andrej Bauer
Enteros complejos bien, pero estaba pidiendo números complejos. Por otra parte, por supuesto, nunca se pueden representar, ya que son incontables.
HdM
@AndrejBauer: muy buen truco (tal vez no tan simple) HdM: seguro que pueden, incluso en no todos. Pero pensé que el método para construir cosas en el cálculo λ con codificación de Church era más importante / apropiado aquí.
jmad
Desearía poder dar dos respuestas correctas =) ¡Ni siquiera estaba pensando que los reales podrían representarse cuando pregunté sobre números complejos, pero ahí lo tienes!
zcaudate