Λ-rachunek lub rachunek lambda, to system logiczny na podstawie funkcji anonimowych. Na przykład jest to wyrażenie λ:
λf.(λx.xx)(λx.f(xx))
Jednak na potrzeby tego wyzwania uprościmy notację:
- Zmień
λ
na\
(aby ułatwić pisanie):\f.(\x.xx)(\x.f(xx))
.
W nagłówkach lambda jest niepotrzebny, więc możemy go upuścić:\f(\xxx)(\xf(xx))
- Użyj notacji prefiksowej w stylu Unlambda z
`
do aplikacji zamiast pisać dwie funkcje razem (aby uzyskać pełne wyjaśnienie, jak to zrobić, zobacz Konwersja między notacjami Lambda Calculus ):\f`\x`xx\x`f`xx
- To najbardziej skomplikowana zamiana. Zastąp każdą zmienną liczbą w nawiasach, zależnie od tego, jak głęboko zagnieżdżona jest zmienna względem nagłówka lambda, do którego należy (tj. Użyj indeksowania De Bruijn opartego na 0 ). Na przykład w
\xx
(funkcja tożsamości)x
treść zostanie zastąpiona przez[0]
, ponieważ należy ona do pierwszego (opartego na 0) nagłówka napotkanego podczas przechodzenia do wyrażenia od zmiennej do końca;\x\y``\x`xxxy
zostanie zamieniony na\x\y``\x`[0][0][1][0]
. Możemy teraz upuścić zmienne w nagłówkach, pozostawiając\\``\`[0][0][1][0]
.
Logika kombinacyjna to w zasadzie Tarpit Turinga wykonany z rachunku λ (właściwie to był pierwszy, ale tutaj nie ma to znaczenia).
„Logikę kombinacyjną można postrzegać jako wariant rachunku lambda, w którym wyrażenia lambda (reprezentujące abstrakcję funkcjonalną) są zastępowane ograniczonym zestawem kombinatorów, prymitywnych funkcji, w których nie ma powiązanych zmiennych.” 1
Najczęstszym rodzajem logiki kombinatorycznej jest rachunek kombinatoryczny SK , który wykorzystuje następujące operacje podstawowe:
K = λx.λy.x
S = λx.λy.λz.xz(yz)
Czasami I = λx.x
dodawany jest kombinator , ale jest on zbędny, ponieważ SKK
(lub SKx
dla dowolnego x
) jest równoważny I
.
Wszystko czego potrzebujesz to K, S i aplikacja, aby móc zakodować dowolne wyrażenie w rachunku λ. Jako przykład, oto tłumaczenie z funkcji λf.(λx.xx)(λx.f(xx))
na logikę kombinacyjną:
λf.(λx.xx)(λx.f(xx)) = S(K(λx.xx))(λf.λx.f(xx))
λx.f(xx) = S(Kf)(S(SKK)(SKK))
λf.λx.f(xx) = λf.S(Kf)(S(SKK)(SKK))
λf.S(Sf)(S(SKK)(SKK)) = S(λf.S(Sf))(K(S(SKK)(SKK)))
λf.S(Sf) = S(KS)S
λf.λx.f(xx) = S(S(KS)S)(K(S(SKK)(SKK)))
λx.xx = S(SKK)(SKK)
λf.(λx.xx)(λx.f(xx)) = S(K(S(SKK)(SKK)))(S(S(KS)S)(K(S(SKK)(SKK))))
Ponieważ używamy notacji przedrostkowej, tak jest ```S`K``S``SKK``SKK``S``S`KSS`K``SKK`
.
1 Źródło: Wikipedia
Wyzwanie
Do tej pory prawdopodobnie zgadłeś, co to jest: Napisz program, który przyjmuje poprawne wyrażenie λ (w opisie powyżej opisanym) jako dane wejściowe i wyjściowe (lub zwraca) tę samą funkcję, przepisaną w rachunku SK-kombinatory. Zauważ, że istnieje nieskończona liczba sposobów na przepisanie tego; potrzebujesz tylko jednego z nieskończonych sposobów.
To jest golf golfowy , więc wygrywa najkrótsze prawidłowe zgłoszenie (mierzone w bajtach).
Przypadki testowe
Każdy przypadek testowy pokazuje jedno możliwe wyjście. Wyrażenie na górze jest równoważnym wyrażeniem rachunku λ.
λx.x:
\[0] -> ``SKK
λx.xx:
\`[0][0] -> ```SKK``SKK
λx.λy.y:
\\[0] -> `SK
λx.λy.x:
\\[1] -> K
λx.λy.λz.xz(yz):
\\\``[2][0]`[1][0] -> S
λw.w(λx.λy.λz.xz(yz))(λx.λy.x):
\``[0]\\[1]\\\``[2][0]`[1][0] -> ``S``SI`KS`KK
λx.f(xx) = S(Kf)(SKK)
? Nie powinno tak być λx.f(xx) = S(Kf)(SII) = S(Kf)(S(SKK)(SKK))
? Podczas konwersji λx.f(xx)
otrzymuję wartość S {λx.f} {λx.xx}
redukującą do, S (Kf) {λx.xx}
a wyrażenie w nawiasach jest niczym innym jak ω=λx.xx
, co, jak wiemy, jest reprezentowane SII = S(SKK)(SKK)
, prawda?
SII
, że nie SKK
. To był błąd.