2021.02.19 05:38
reprezentacja Churcha w javaskrypcie
Jakiś czas temu dla zabawy spróbowałem zaimplementować w Javaskrypcie reprezentację Churcha liczb naturalnych. Proszę, oto ona:
const zero = (funkcja, start) => start
const nastepnik = czego => ((funkcja, start) => funkcja(czego(funkcja, start)))
const jeden = nastepnik(zero)
const dwa = nastepnik(jeden)
const trzy = nastepnik(dwa)
const gwiazdki = liczba => liczba(zapis => zapis + '*', '')
const dodaj = (a, b) => a(nastepnik, b)
const pomnoz = (a, b) => a(x => dodaj(x, b), zero)
const prawda = (a, b) => a
const falsz = (a, b) => b
const boolNaNapis = b => b('prawda', 'fałsz')
const czyZero = (liczba) => liczba(_ => falsz, prawda)
const oraz = (a, b) => a(b(prawda, falsz), falsz)
const lub = (a, b) => a(prawda, b(prawda, falsz))
const nie = (a) => a(falsz, prawda)
const para = (a, b) => (selektor => selektor(a, b))
const poprzednik = liczba => {
// każda para ma dwa elementy: pewną liczbę oraz jej następnik
const dojdzParamiDoLiczby = liczba => liczba(
poprzedniaPara => {
const czyPierwszaPara = oraz(czyZero(poprzedniaPara(prawda)), czyZero(poprzedniaPara(falsz)))
return czyPierwszaPara(para(zero, jeden), para(nastepnik(poprzedniaPara(prawda)), nastepnik(poprzedniaPara(falsz))))
},
para(zero, zero)
)
return dojdzParamiDoLiczby(liczba)(prawda)
}
const odejmij = (a, b) => b(poprzednik, a)
// Tak się oczywiście nie da: zapomnij o rekurencji, jak nie masz ani kawałka leniwej ewaluacji.
/*
const czyMniejsza = (a, b) => oraz(czyZero(a), czyZero(b))(
falsz,
czyZero(a)(
prawda,
czyZero(
b,
czyMniejsza(poprzednik(a), poprzednik(b))
)
)
)
*/
const petlaZeStopem = (ileRazy, coZrobic, sprawdzCzyZatrzymac, stanPoczatkowy) => ileRazy(
poprzedniaPara => {
const poprzedniStan = poprzedniaPara(prawda)
const poprzednieCzyZatrzymac = poprzedniaPara(falsz)
return poprzednieCzyZatrzymac(
poprzedniaPara,
(() => {
const czyZatrzymac = sprawdzCzyZatrzymac(poprzedniStan)
const nowyStan = coZrobic(poprzedniStan)
return para(nowyStan, czyZatrzymac)
})()
)
},
para(stanPoczatkowy, falsz)
)
const czyRowne = (a, b) => {
const sprawdzCzyRowne = (a, b) => petlaZeStopem(
dodaj(jeden, dodaj(a, b)), // jeśli nie dodam tej jedynki, to jeli oba argumenty będą zerami, nie wykona się ani jeden obieg pętli i nie dowiemy się, że są równe
poprzedniStan => {
const licznikA = poprzedniStan(prawda)(prawda)
const licznikB = poprzedniStan(prawda)(falsz)
const rowne = oraz(czyZero(licznikA), czyZero(licznikB))
return para(para(poprzednik(licznikA), poprzednik(licznikB)), rowne)
},
poprzedniStan => {
const licznikA = poprzedniStan(prawda)(prawda)
const licznikB = poprzedniStan(prawda)(falsz)
return lub(czyZero(licznikA), czyZero(licznikB))
},
para(para(a, b), falsz)
)
return sprawdzCzyRowne(a, b)(prawda)(falsz)
}
const czyWieksze = (a, b) => oraz(
nie(czyRowne(a, b)),
czyZero(odejmij(b, a))
)
const czyMniejsze = (a, b) => oraz(
nie(czyRowne(a, b)),
czyZero(odejmij(a, b))
)
const podzielZReszta = (dzielna, dzielnik) => {
const szukajIlorazuIReszty = (dzielna, dzielnik) => petlaZeStopem(
dzielna,
poprzedniStan => {
const licznikDzielnej = poprzedniStan(prawda)(prawda)
const dzielnik = poprzedniStan(prawda)(falsz)
const dotychczasowyIloraz = poprzedniStan(falsz)(prawda)
const licznikDzielnejMniejszyOdDzielnika = czyMniejsze(licznikDzielnej, dzielnik)
return licznikDzielnejMniejszyOdDzielnika(
para(para(licznikDzielnej, dzielnik), para(dotychczasowyIloraz, licznikDzielnej)),
para(para(odejmij(licznikDzielnej, dzielnik), dzielnik), para(nastepnik(dotychczasowyIloraz), zero))
)
},
poprzedniStan => {
const dzielna = poprzedniStan(prawda)(prawda)
const dzielnik = poprzedniStan(prawda)(falsz)
return czyMniejsze(dzielna, dzielnik)
},
para(para(dzielna, dzielnik), para(zero, zero))
)
return szukajIlorazuIReszty(dzielna, dzielnik)(prawda)(falsz)
}
const czyPierwsza = liczba => {
return poprzednik(poprzednik(liczba))(
aktualnaPara => {
const aktualnyDzielnik = aktualnaPara(prawda)
const dotychczasowyWynik = aktualnaPara(falsz)
const nieDzieliSie = nie(czyZero(podzielZReszta(liczba, aktualnyDzielnik)(falsz)))
const nowyWynik = oraz(dotychczasowyWynik, nieDzieliSie)
return para(nastepnik(aktualnyDzielnik), nowyWynik)
},
para(dwa, prawda)
)(falsz)
}
// tu kończy się prawdziwy kod, a zaczyna się sprawdzanie, czy to wszystko dobrze działa
console.log(gwiazdki(trzy))
const piec = dodaj(dwa, trzy)
console.log(gwiazdki(piec))
console.log(gwiazdki(pomnoz(trzy, piec)))
console.log(boolNaNapis(prawda))
console.log(boolNaNapis(falsz))
console.log(boolNaNapis(czyZero(zero)))
console.log(boolNaNapis(czyZero(trzy)))
console.log(boolNaNapis(oraz(czyZero(zero), czyZero(zero))))
console.log(boolNaNapis(oraz(czyZero(zero), czyZero(trzy))))
console.log(boolNaNapis(lub(czyZero(zero), czyZero(trzy))))
console.log('-----')
const mojaPara = para(3, 5)
console.log(mojaPara(prawda))
console.log(mojaPara(falsz))
console.log(gwiazdki(poprzednik(piec)))
console.log(gwiazdki(odejmij(piec, dwa)))
console.log(boolNaNapis(czyRowne(zero, trzy)))
console.log(boolNaNapis(czyWieksze(trzy, zero)))
console.log(boolNaNapis(czyWieksze(trzy, trzy)))
console.log(boolNaNapis(czyMniejsze(trzy, trzy)))
console.log(boolNaNapis(czyMniejsze(dwa, trzy)))
console.log(boolNaNapis(czyMniejsze(piec, trzy)))
const zapis = liczba => liczba(x => x + 1, 0)
const wynik = podzielZReszta(piec, trzy)
const iloraz = wynik(prawda)
const reszta = wynik(falsz)
console.log(zapis(iloraz))
console.log(zapis(reszta))
console.log(boolNaNapis(czyPierwsza(dodaj(piec, jeden))))
console.log(boolNaNapis(czyPierwsza(dodaj(piec, dwa))))
komentarze:
powrót na stronę główną
RSS