Λ-исчисление , или лямбда - исчисление, является логической системой , основанной на анонимных функциях. Например, это λ-выражение:
λf.(λx.xx)(λx.f(xx))
Однако для целей этой задачи мы упростим обозначение:
- Измените
λна\(чтобы было легче набирать текст):\f.(\x.xx)(\x.f(xx)) .В лямбда - заголовков не требуется, так что мы можем бросить его:\f(\xxx)(\xf(xx))- Используйте префикс Unlambda -style
`для приложения, вместо того, чтобы писать две функции вместе (полное объяснение того, как это сделать, см. В разделе Преобразование нотаций лямбда-исчисления ):\f`\x`xx\x`f`xx - Это самая сложная замена. Замените каждую переменную числом в скобках в зависимости от того, насколько глубоко вложенная переменная относится к лямбда-заголовку, к которому она принадлежит (т. Е. Используйте индексацию по Де-Брюину на основе 0 ). Например, в
\xx(функция идентификации)xтело в теле будет заменено на[0], поскольку оно принадлежит первому (основанному на 0) заголовку, встречающемуся при прохождении выражения от переменной до конца;\x\y``\x`xxxyбудет преобразован в\x\y``\x`[0][0][1][0]. Теперь мы можем отбросить переменные в заголовках, оставив\\``\`[0][0][1][0].
Комбинаторная логика - это, по сути, тарпит Тьюринга, составленный из λ-исчисления (ну, на самом деле, он появился первым, но здесь это не имеет значения.)
«Комбинаторную логику можно рассматривать как вариант лямбда-исчисления, в котором лямбда-выражения (представляющие функциональную абстракцию) заменяются ограниченным набором комбинаторов, примитивных функций, из которых отсутствуют связанные переменные». 1
Наиболее распространенным типом комбинаторной логики является комбинаторное исчисление SK , в котором используются следующие примитивы:
K = λx.λy.x
S = λx.λy.λz.xz(yz)
Иногда добавляется комбинатор I = λx.x, но он является избыточным, поскольку SKK(или даже SKxдля любого x) эквивалентен I.
Все, что вам нужно, это K, S и приложение, чтобы иметь возможность кодировать любое выражение в λ-исчислении. Например, вот перевод из функции λf.(λx.xx)(λx.f(xx))в комбинаторную логику:
λ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))))
Так как мы используем префиксную нотацию, это так ```S`K``S``SKK``SKK``S``S`KSS`K``SKK`.
1 Источник: Википедия
Соревнование
К настоящему времени вы, вероятно, уже догадались, что: написать программу, которая принимает действительное λ-выражение (в обозначении, описанном выше) в качестве входных данных и выводит (или возвращает) ту же функцию, переписанную в исчислении SK-комбинатора. Обратите внимание, что существует бесконечное количество способов переписать это; вам нужно только вывести один из бесконечных способов.
Это код-гольф , поэтому выигрывает самое короткое действительное представление (измеряется в байтах).
Тестовые случаи
Каждый тестовый пример показывает один возможный вывод. Выражение сверху является эквивалентным выражением λ-исчисления.
λ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)? Разве это не должно быть λx.f(xx) = S(Kf)(SII) = S(Kf)(S(SKK)(SKK))? При преобразовании λx.f(xx)я получаю значение, S {λx.f} {λx.xx}которое сводится к, S (Kf) {λx.xx}а выражение в скобках - не что иное ω=λx.xx, как SII = S(SKK)(SKK), как мы знаем, представляется как , верно?
SII, нет SKK. Это была ошибка.