Reprezentowanie liczb ujemnych i zespolonych za pomocą rachunku Lambda

14

Większość samouczków na temat rachunku Lambda stanowi przykład, w którym dodatnie liczby całkowite i liczby boolowskie mogą być reprezentowane przez funkcje. Co z -1 i ja?

zcaudate
źródło

Odpowiedzi:

18

Najpierw zakoduj liczby naturalne i pary, jak opisano w jmad.

Reprezentują liczbę całkowitą jako parę liczb naturalnych takich, że . Następnie możesz zdefiniować zwykłe operacje na liczbach całkowitych jako (używając notacji Haskell dla -calculus):( a , b ) k = a - b λk(za,b)k=za-bλ

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)

Przypadek liczb zespolonych jest podobny w tym sensie, że liczba zespolona jest kodowana jako para liczb rzeczywistych. Ale bardziej skomplikowanym pytaniem jest, jak zakodować rzeczywiste. Tutaj musisz wykonać więcej pracy:

  1. Zakoduj liczbę wymierną jako parę gdzie jest liczbą całkowitą, jest naturalne, a .( k , a ) k a q = k / ( 1 + a )q(k,a)kaq=k/(1+a)
  2. Zakoduj liczbę rzeczywistą za pomocą funkcji tak że dla każdego naturalnego , koduje liczbę wymierną tak że . Innymi słowy, rzeczywistość jest kodowana jako sekwencja racjonałów zbiegających się z nią w tempie .f k N f k q | x - q | < 2 - k k 2 - kxfkNfkq|xq|<2kk2k

Kodowanie reali to dużo pracy i nie chcesz tego robić w -calculus. Ale zobacz na przykład podkatalog Marshall, aby zobaczyć prostą implementację wartości rzeczywistych w czystym Haskell. Można to w zasadzie przetłumaczyć na czysty -calculus.λλetc/haskellλ

Andrej Bauer
źródło
1
Wow =) Intuicyjnie zastanawiam się, co to znaczy ... na przykład, używając kodowania liczb kościelnych ... tj. liczba kościelna liczby całkowitej n jest reprezentowana przez funkcję, która stosuje funkcję do wartości n razy. Czy pary i ujemne wartości lambda mają podobne intuicyjne odczucia?
zcaudate
1
Kodowanie kościelne koduje liczby naturalne , 1 , 2 , ... Nie koduje liczb ujemnych. W powyższej odpowiedzi założyłem, że wiesz już o kodowaniu liczb naturalnych, więc wyjaśniłem, jak uzyskać liczby całkowite. Zakodowane przeze mnie liczby całkowite są bardziej formalną konstrukcją, w przeciwieństwie do liczb kościelnych, które są ściślej powiązane z rachunkiem λ . Nie sądzę, by „ujemne wartości lambda” były znaczącym zwrotem. 012λ
Andrej Bauer
@zcaudate [TYP Objaśnienia: i:ℤ, x:a, f,u,s:a→a, p:(a→a,a→a)] W przypadku kodowania ℤ a (Sign,ℕ)następnie, ze względu parę funkcji (s,f)jak ptermin λi.λp.λx.(fst i) (fst p) id ((snd i) (snd p) x)spowoduje zarówno f(…f(x)…)lub s(f(…f(x)…))(jeśli wynik jest ujemny). Jeśli kodujesz ℤ as (ℕ,ℕ), potrzebujesz funkcji, która ma odwrotność - biorąc pod uwagę parę (f,u)i x, funkcja λi.λp.λx.(snd i)(snd p)((fst i)(fst p) x)wygeneruje, u(…u(f(…f(x)…))…)co pozostawi fzastosowane iczasy x. Oba działają w różnych kontekstach (wynik można „odwrócić” || fjest odwracalny).
nikt
@zcaudate Dodatkowe funkcje są konieczne, ponieważ liczby zakodowane w Kościele „powtarzają się same”, ale pary przekażą ci tylko ich elementy. Funkcje pomocnika po prostu sklejają komponenty w „właściwej” kolejności (co dzieje się automatycznie dla nats.) Zobacz także: en.wikipedia.org/wiki/… - Kodowanie w kościele jest w zasadzie fold . ctordla każdego konstruktora i tego typu fold( r). (Dlatego w przypadku typów rekurencyjnych dane „same się powtarzają”. W przypadku typów nierekurencyjnych bardziej przypomina casedopasowanie / wzorzec.)
nikt
13

Rachunek Lambda może kodować większość struktur danych i podstawowe typy. Na przykład, możesz zakodować parę istniejących terminów w rachunku lambda, używając tego samego kodowania Kościoła , które zwykle widzisz, aby zakodować nieujemne liczby całkowite i logiczne:

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

para=λxyz.zxy
pierwszy=λp.p(λxy.x)
snd=λp.p(λxy.y)

Zatem para to p = ( para  a b ), a jeśli chcesz odzyskać a i b , możesz zrobić ( fst  p ) i ( snd  p ) .(za,b)p=(para zab)zab(pierwszy p)(snd p)

Oznacza to, że możesz łatwo przedstawić dodatnie i ujemne liczby całkowite za pomocą pary: znak po lewej stronie i wartość bezwzględna po prawej stronie. Znak jest wartością logiczną, która określa, czy liczba jest dodatnia. Po prawej jest liczba naturalna z wykorzystaniem kodowania Church.

(sjasoln,n)

A teraz, gdy masz względne liczby całkowite. Mnożenie jest łatwe do zdefiniowania, po prostu trzeba zastosować funkcjixor na znak i mnożenia na liczbach naturalnych w wartości bezwzględnej:

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

Aby zdefiniować dodanie, musisz porównać dwie liczby naturalne i użyć odejmowania, gdy znaki są różne, więc nie jest to termin λ, ale możesz go dostosować, jeśli naprawdę chcesz:

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|

ale wtedy odejmowanie jest naprawdę łatwe do zdefiniowania:

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

Gdy masz dodatnie i ujemne liczby całkowite, możesz bardzo łatwo zdefiniować liczby całkowite złożone : jest to tylko para dwóch liczb całkowitych które reprezentują . Zatem dodawanie jest punktowe, a mnożenie jest jak zwykle , ale nie będę tego pisać, powinno być łatwo:(a,b)a+bi

add[i]=λz1z2.pair(add(fst z1)(fst z2))(add(snd z1)(snd z2))
jmad
źródło
6
k(a,b)k=ab
Złożone liczby całkowite w porządku, ale prosił o liczby zespolone. Z drugiej strony, oczywiście, nigdy nie mogą być reprezentowane, ponieważ są niepoliczalne.
HdM
@AndrejBauer: bardzo fajna sztuczka (być może nie taka prosta) HdM: na pewno mogą, nawet nie we wszystkich. Ale pomyślałem, że metoda budowania rzeczy w rachunku λ z kodowaniem Churcha była tutaj ważniejsza / odpowiedniejsza.
jmad
Chciałbym móc podać dwie poprawne odpowiedzi =) Nawet nie myślałem, że rzeczywiste mogą być reprezentowane, kiedy pytałem o liczby zespolone, ale proszę bardzo!
zcaudate