Der λ-Kalkül oder Lambda-Kalkül ist ein logisches System, das auf anonymen Funktionen basiert. Zum Beispiel ist dies ein λ-Ausdruck:
λf.(λx.xx)(λx.f(xx))
Für diese Herausforderung vereinfachen wir jedoch die Notation:
- Ändern Sie
λ
zu\
(um die Eingabe zu vereinfachen):\f.(\x.xx)(\x.f(xx))
- Die
.
In-Lambda-Header sind nicht erforderlich, daher können wir sie löschen:\f(\xxx)(\xf(xx))
- Verwenden Sie die unlambda -Stil Präfixnotation mit
`
für die Anwendung , anstatt die beiden Funktionen zusammen zu schreiben (für eine vollständige Erklärung, wie dies zu tun, siehe Konvertieren zwischen Lambda - Kalkül Notationen ):\f`\x`xx\x`f`xx
- Dies ist die komplizierteste Substitution. Ersetzen Sie jede Variable durch eine Zahl in Klammern, je nachdem, wie tief die Variable im Verhältnis zum Lambda-Header verschachtelt ist, zu dem sie gehört (dh verwenden Sie die 0-basierte De Bruijn-Indizierung ). Zum Beispiel würde in
\xx
(der Identitätsfunktion)x
der Text im Text durch ersetzt[0]
, da er zum ersten (0-basierten) Header gehört, der beim Durchlaufen des Ausdrucks von der Variablen bis zum Ende angetroffen wird.\x\y``\x`xxxy
würde umgewandelt werden in\x\y``\x`[0][0][1][0]
. Wir können nun die Variablen in den Headern ablegen und verlassen\\``\`[0][0][1][0]
.
Die kombinatorische Logik ist im Grunde ein Turing Tarpit aus dem λ-Kalkül (Nun, eigentlich kam es zuerst, aber das ist hier irrelevant.)
"Kombinatorische Logik kann als eine Variante des Lambda-Kalküls angesehen werden, bei der Lambda-Ausdrücke (die die funktionale Abstraktion darstellen) durch eine begrenzte Menge von Kombinatoren ersetzt werden, primitive Funktionen, bei denen gebundene Variablen fehlen." 1
Der gebräuchlichste Typ der kombinatorischen Logik ist der SK-Kombinator-Kalkül , der die folgenden Grundelemente verwendet:
K = λx.λy.x
S = λx.λy.λz.xz(yz)
Manchmal wird ein Kombinator I = λx.x
hinzugefügt, der jedoch überflüssig ist, da SKK
(oder tatsächlich SKx
für jeden x
) er äquivalent ist I
.
Alles, was Sie brauchen, ist K, S und application, um einen beliebigen Ausdruck im λ-Kalkül zu kodieren. Als Beispiel folgt eine Übersetzung von der Funktion λf.(λx.xx)(λx.f(xx))
in die kombinatorische Logik:
λ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))))
Da wir die Präfixnotation verwenden, ist dies ```S`K``S``SKK``SKK``S``S`KSS`K``SKK`
.
1 Quelle: Wikipedia
Die Herausforderung
Inzwischen haben Sie wahrscheinlich erraten, was ist: Schreiben Sie ein Programm, das einen gültigen λ-Ausdruck (in der oben beschriebenen Notation) als Eingabe verwendet und dieselbe Funktion ausgibt (oder zurückgibt), die in der SK-Kombinator-Rechnung umgeschrieben wurde. Beachten Sie, dass es unendlich viele Möglichkeiten gibt, dies umzuschreiben. Sie müssen nur eine der unendlichen Möglichkeiten ausgeben.
Dies ist Code-Golf , also gewinnt die kürzeste gültige Übermittlung (gemessen in Bytes).
Testfälle
Jeder Testfall zeigt eine mögliche Ausgabe. Der Ausdruck oben ist der äquivalente λ-Kalkülausdruck.
λ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)
? Sollte es nicht lieber sein λx.f(xx) = S(Kf)(SII) = S(Kf)(S(SKK)(SKK))
? Beim Konvertieren λx.f(xx)
bekomme ich S {λx.f} {λx.xx}
was reduziert S (Kf) {λx.xx}
und der Ausdruck in Klammern ist nichts anderes als das ω=λx.xx
, was wir als dargestellt wissen SII = S(SKK)(SKK)
, oder?
SII
, nicht SKK
. Das war ein Fehler.