I numeri di chiesa sono una codifica di numeri naturali come funzioni.
(\ f x → (f x)) -- church number 1
(\ f x → (f (f (f x)))) -- church number 3
(\ f x → (f (f (f (f x))))) -- church number 4
Ordinariamente, puoi esponenziare 2 numeri di chiesa semplicemente applicandoli. Cioè, se si applica da 4 a 2, si ottiene il numero della chiesa 16
o2^4
. Ovviamente, questo è assolutamente poco pratico. I numeri delle chiese hanno bisogno di una quantità lineare di memoria e sono molto, molto lenti. Il calcolo di qualcosa del genere 10^10
- a cui GHCI risponde rapidamente in modo corretto - richiederebbe anni e non potrebbe comunque contenere la memoria del computer.
Ultimamente ho sperimentato con valutatori λ ottimali. Nei miei test, ho accidentalmente digitato quanto segue sul mio calcolatore λ ottimale:
10 ^ 10 % 13
Doveva essere moltiplicazione, non esponenziazione. Prima che potessi muovere le dita per interrompere disperatamente il programma in esecuzione per sempre, rispose alla mia richiesta:
3
{ iterations: 11523, applications: 5748, used_memory: 27729 }
real 0m0.104s
user 0m0.086s
sys 0m0.019s
Con il mio "avviso bug" lampeggiante, sono andato su Google e verificato, 10^10%13 == 3
anzi. Ma il calcolatore λ non doveva trovare quel risultato, riusciva a malapena a memorizzare 10 ^ 10. Ho iniziato a sottolinearlo, per la scienza. Mi ha risposto immediatamente 20^20%13 == 3
, 50^50%13 == 4
, 60^60%3 == 0
. Ho dovuto usare strumenti esterni per verificare quei risultati, dal momento che Haskell stesso non era in grado di calcolarlo (a causa del trabocco di numeri interi) (è se usi Integer non Ints, ovviamente!). Spingendolo ai suoi limiti, questa era la risposta a 200^200%31
:
5
{ iterations: 10351327, applications: 5175644, used_memory: 23754870 }
real 0m4.025s
user 0m3.686s
sys 0m0.341s
Se avessimo una copia dell'universo per ogni atomo nell'universo e avessimo un computer per ogni atomo che avevamo in totale, non potremmo memorizzare il numero della chiesa 200^200
. Questo mi ha spinto a chiedermi se il mio mac fosse davvero così potente. Forse il valutatore ottimale è stato in grado di saltare i rami non necessari e arrivare alla risposta nello stesso modo che Haskell fa con una valutazione pigra. Per provare questo, ho compilato il programma λ su Haskell:
data Term = F !(Term -> Term) | N !Double
instance Show Term where {
show (N x) = "(N "++(if fromIntegral (floor x) == x then show (floor x) else show x)++")";
show (F _) = "(λ...)"}
infixl 0 #
(F f) # x = f x
churchNum = F(\(N n)->F(\f->F(\x->if n<=0 then x else (f#(churchNum#(N(n-1))#f#x)))))
expMod = (F(\v0->(F(\v1->(F(\v2->((((((churchNum # v2) # (F(\v3->(F(\v4->(v3 # (F(\v5->((v4 # (F(\v6->(F(\v7->(v6 # ((v5 # v6) # v7))))))) # v5))))))))) # (F(\v3->(v3 # (F(\v4->(F(\v5->v5)))))))) # (F(\v3->((((churchNum # v1) # (churchNum # v0)) # ((((churchNum # v2) # (F(\v4->(F(\v5->(F(\v6->(v4 # (F(\v7->((v5 # v7) # v6))))))))))) # (F(\v4->v4))) # (F(\v4->(F(\v5->(v5 # v4))))))) # ((((churchNum # v2) # (F(\v4->(F(\v5->v4))))) # (F(\v4->v4))) # (F(\v4->v4))))))) # (F(\v3->(((F(\(N x)->F(\(N y)->N(x+y)))) # v3) # (N 1))))) # (N 0))))))))
main = print $ (expMod # N 5 # N 5 # N 4)
Questo genera correttamente 1
(5 ^ 5 % 4
) - ma lancia qualcosa sopra 10^10
e rimarrà bloccato, eliminando l'ipotesi.
Il valutatore ottimale che ho usato è un programma JavaScript lungo 160 righe non ottimizzato che non includeva alcun tipo di matematica del modulo esponenziale - e la funzione del modulo lambda-calcolo che ho usato era altrettanto semplice:
(λab.(b(λcd.(c(λe.(d(λfg.(f(efg)))e))))(λc.(c(λde.e)))(λc.(a(b(λdef.(d(λg.(egf))))(λd.d)(λde.(ed)))(b(λde.d)(λd.d)(λd.d))))))
Non ho usato alcun algoritmo o formula aritmetica modulare specifica. Quindi, in che modo il valutatore ottimale è in grado di arrivare alle risposte giuste?
node test.js
. Fatemi sapere se avete domande.