Обратное преобразование в Прологе
Хотел бы сделать следующее обратное преобразование из выражений SKI в лямбда-выражения:
L[I] = λx.x
L[K] = λx.λy.x
L[S] = λx.λy.λz.(x z (y z))
L[(E₁ E₂)] = (L[E₁] L[E₂])
Преобразование не обязательно должно включать какое-либо бета-восстановление. Но хотелось бы все же сделать специальную бета-редукцию. Всякий раз, когда есть линейный редекс или единичный редекс:
(λx.E₁)E₂ x occurs at most once in E₁
Я хочу уменьшить его до:
E₁[x/E₂]
Это кажется безопасным сокращением в том смысле, что оно не увеличивает редекс, а только перемещает положение E₂ или даже устраняет E₂, если x не встречается. Соответственно делает ренейм. Пример:
L[S(S(K(S(KS)K))S)(KK)] = λx.λy.λz.xzy
Любая реализация Prolog вокруг?
2 ответа
Достаточно прямой перевод спецификации в представление с логическими переменными:
combinator_lambda(i, lambda(X, X)).
combinator_lambda(k, lambda(X, lambda(_Y, X))).
combinator_lambda(s, lambda(X, lambda(Y, lambda(Z,
apply(apply(X, Z), apply(Y, Z)))))).
combinator_lambda(C1 * C2, Lambda) :-
combinator_lambda(C1, L1),
combinator_lambda(C2, L2),
simplify(apply(L1, L2), Lambda).
simplify(Term, Simple) :-
( nonvar(Term),
Term = apply(X, Y)
-> simplify(X, SimpleX),
simplify(Y, SimpleY),
( nonvar(SimpleX),
SimpleX = lambda(V, Exp),
term_atmostonce(Exp, V)
-> apply(SimpleX, SimpleY, XY),
simplify(XY, Simple)
; Simple = apply(SimpleX, SimpleY) )
; nonvar(Term),
Term = lambda(V, Exp)
-> simplify(Exp, SimpleExp),
Simple = lambda(V, SimpleExp)
; Simple = Term ).
term_atmostonce(Term, NoOccurrence) :-
term_variables(Term, Variables),
forall(member(Var, Variables), Var \== NoOccurrence),
!.
term_atmostonce(Term, Singleton) :-
term_singletons(Term, Singletons),
member(Var, Singletons),
Var == Singleton,
!.
apply(lambda(V, E), V, E).
(
term_singletons
является утилитой SWI-Prolog.)
?- combinator_lambda(s*(s*(k*(s*(k*s)*k))*s)*(k*k), Lambda), numbervars(Lambda).
Lambda = lambda(A, lambda(B, lambda(C, apply(apply(A, C), B)))).
Вот реализация, основанная на индексах deBruijn, и на основе ответов Prolog . Первая часть, перевод L[_] из wikipedia в индексы deBruijn, дает:
lambda(P*Q, W) :-
lambda(P, R),
lambda(Q, S),
simplify(R, S, W).
lambda('I', b(0)).
lambda('K', b(b(1))).
lambda('S', b(b(b(2*0*(1*0))))).
Новые предикаты в линейном упрощении — это simple/2, subst2/4, notin/2 и Oncein/2. shift/4 точно такой же, как в ответах Пролога здесьздесь. Предикат subst2/4, в отличие от subst/4, вызывает simple/2 для построения терминов приложения:
simplify(b(R), _, S) :- notin(R, 0), !,
shift(R, 0, -1, S).
simplify(b(R), S, T) :- oncein(R, 0), !,
subst2(R, 0, S, T).
simplify(R, S, R*S).
subst2(P*Q, J, D, W) :- !,
subst2(P, J, D, R),
subst2(Q, J, D, S),
simplify(R, S, W).
subst2(b(P), J, D, b(R)) :- !,
K is J+1,
subst2(P, K, D, R).
subst2(J, K, _, J) :- integer(J), J < K, !.
subst2(0, 0, D, D) :- !.
subst2(J, J, D, R) :- integer(J), !,
shift(D, 0, J, R).
subst2(J, _, _, K) :- integer(J), !, K is J-1.
subst2(I, _, _, I).
Предикаты notin/2 и Oncein/2 разбивают «не более одного раза» на «не» и «ровно один раз»:
notin(P*Q, J) :- !,
notin(P, J),
notin(Q, J).
notin(b(P), J) :- !,
K is J+1,
notin(P, K).
notin(J, K) :- integer(J), !,
J =\= K.
notin(_, _).
oncein(P*Q, J) :-
notin(P, J), !,
oncein(Q, J).
oncein(P*Q, J) :- !,
oncein(P, J),
notin(Q, J).
oncein(b(P), J) :- !,
K is J+1,
oncein(P, K).
oncein(J, J).
Это вопрос проблема решена:
?- lambda('S'*('S'*('K'*('S'*('K'*'S')*'K'))*'S')*('K'*'K'), X).
X = b(b(b(2*0*1)))
Пример не полностью взаимодействует с этой абстракцией скобок здесь:
?- unlambda(b(b(b(2*0*1))), X).
X = 'S'*('S'*('K'*'S')*('S'*('K'*'K')*'S'))*('K'*'K')