Λ-исчисление , или лямбда - исчисление, является логической системой , основанной на анонимных функциях. Например, это λ-выражение:
λ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
. Это была ошибка.