@false прокомментировал ранее:
Да, вы можете реализовать машину Тьюринга без
dif/2. Но вы даже не можете реализовать пересечение или подобные предикаты.
Предположим, мы расширяем чистый Пролог (Horn FOL + CWA + UNA ) с помощью call/N, dif/2 и (=)/3, которые будут использоваться в if_/3, будут ли по-прежнему пробелы в его выразительности, т.е. вещи, которые тривиально определить, скажем, на Схеме, но гораздо сложнее сформулировать в таком расширенном (почти чистом) Прологе?
В частности, позволяет ли такой Пролог манипулировать списками Пролога так же удобно, как Scheme позволяет манипулировать списками Scheme?
Редактировать: предположим, что схема без мутаций, макросов, продолжений, лени, потоков, чисел, строк, векторов или символов. Просто символы, логические значения и списки (деревья).
@false Я думал о Схеме без чисел, строк или символов. Просто символы. Я отредактирую, чтобы уточнить.





Если брать чистый Scheme. Возможно, достаточно чистого Пролога. Чистая схема будет представлять собой лямбда-выражения с определенной строгой стратегией оценки вызова по значению. Таким образом, мы могли бы реализовать чистую схему следующим образом на Прологе, опираясь на индексы де Брейна:
eval(P*Q, H) :- !,
eval(P, R),
eval(Q, S),
reduce(R, S, H).
eval(X, X).
reduce(b(R), S, J) :- !,
subst(R, 0, S, H),
eval(H, J).
reduce(R, S, R*S).
Если вы немного измените представление, я думаю, вы сможете избавиться от сокращений. И, возможно, сделать необходимую арифметику с помощью аксиом Пеано. Эх, вуаля, вы получили чистую схему на чистом Прологе.
Вот пример запроса, SUCC и ZERO взяты из здесь:
?- ZERO = b(b(0)), SUCC = b(b(b(1*(2*1*0)))),
eval(SUCC*(SUCC*ZERO)*f*a, X).
ZERO = b(b(0)),
SUCC = b(b(b(1*(2*1*0)))),
X = f*(f*a)
В Scheme вы можете тривиально проверить равенство if-then-elseish. Не так в Прологе без dif/2.
Удачи с dif/2, в SWI-Prolog вы не можете комбинировать его с unify_with_occurs_check/2, см. здесь: stackoverflow.com/q/65532899/502187
Просто символы и списки (деревья).
Вам также нужны логические значения схемы #t и #f, если вы не хотите кодировать все в чистом лямбда-исчислении. Вы также исключаете значения функций, что, к счастью, упрощает этот ответ. Хотя вам придется разрешить частный случай форм верхнего уровня (define name (lambda ...)). (Все остальное, включая расширенные let выражения, может быть дефункционализировано.)
Итак, мое утверждение таково: нет, между этим расплывчатым подмножеством Схемы и чистым Прологом, как вы его определили, нет разрыва в выразительности. Мой аргумент (это не доказательство) конструктивен, он переводит код схемы для пересечения списка из этого ответа в Пролог.
В частности, это:
(define intersect
(lambda (set1 set2)
(cond
((null? set1)(quote ()))
((member? (car set1) set2)
(cons (car set1)
(intersect (cdr set1) set2)))
(else (intersect (cdr set1) set2)))))
становится:
intersect(Set1, Set2, Result) :-
cond([
['null?'(Set1), result([])],
[cond_1(Set1, Set2), body_1(Set1, Set2)],
[else, body_2(Set1, Set2)]], Result).
cond_1(Set1, Set2, Result) :-
car(Set1, Car),
'member?'(Car, Set2, Result).
body_1(Set1, Set2, Result) :-
car(Set1, Car),
cdr(Set1, Cdr),
intersect(Cdr, Set2, PartialIntersection),
cons(Car, PartialIntersection, Result).
body_2(Set1, Set2, Result) :-
cdr(Set1, Cdr),
intersect(Cdr, Set2, Result).
и это:
(define member?
(lambda (a lat)
(cond
((null? lat) #f)
(else (or (equal? (car lat) a)
(member? a (cdr lat)))))))
становится:
'member?'(A, Lat, Result) :-
cond([
['null?'(Lat), result('#f')],
[else, or([or_case_1(Lat, A),
or_case_2(Lat, A)])]], Result).
or_case_1(Lat, A, Result) :-
car(Lat, Car),
'equal?'(Car, A, Result).
or_case_2(Lat, A, Result) :-
cdr(Lat, Cdr),
'member?'(A, Cdr, Result).
Обратите внимание, что вложенные выражения должны быть невложенными, и во всех случаях, кроме самых тривиальных, это проще всего сделать, определив вспомогательные предикаты Пролога. Это не увеличивает размер кода нелинейно.
В этих определениях используются следующие переводы стандартных конструкций Scheme:
'equal?'(X, Y, '#t') :-
=(X, Y, true).
'equal?'(X, Y, '#f') :-
=(X, Y, false).
'null?'(Value, Result) :-
'equal?'(Value, [], Result).
car([Car | _Cdr], Car).
cdr([_Car | Cdr], Cdr).
cons(Car, Cdr, [Car | Cdr]).
or([], '#f').
or([Goal | Goals], Result) :-
if (Goal,
Result = '#t',
or(Goals, Result)).
cond([], _Result) :-
throw(error(cond_without_else, _)).
cond([[Condition, Body] | OtherCases], Result) :-
if (Condition,
call(Body, Result),
cond(OtherCases, Result)).
Некоторые вспомогательные материалы для получения простого значения из тела случая cond и для случая else:
result(Result, Result).
else('#t').
И это вся внутренне нечистая, внешне чистая поддержка Пролога, которая вам нужна:
if (Goal, True, False) :-
call(Goal, Truth),
( Truth == '#t' -> call(True)
; Truth == '#f' -> call(False)
; throw(error(type_or_instantiation_error(Truth), _)) ).
Я назвал это if/3 вместо if_/3, потому что это не совсем «стандарт» if_/3: он ожидает, что условие будет оцениваться как истинное значение схемы, а не true или false. Не стесняйтесь массировать его до «стандартной» формы. Обновлено: есть несколько «достаточно хороших» способов определить (=)/3, которые будут работать в контексте этого ответа, но чтобы избежать дальнейшего отказа от велосипедов, просто используйте определение из https://stackoverflow.com/a/27358600/4391743.
Тесты:
?- 'member?'(a, [x, y, a, c], Result).
Result = '#t' ;
false.
?- intersect([a, b, c, d], [c, d, e, f], Result).
Result = [c, d] ;
false.
Это (=)/3 не чисто.
Это достаточно чисто, если предположить, что все значения Схемы зашлифованы. Я хотел добавить это в ответ, я обновлю его. В любом случае, замена моего (=)/3 вашим полным определением из stackoverflow.com/a/27358600/4391743 заставляет программу вести себя так же.
Нет необходимости в разнице/2. Вместо этого ошибка создания экземпляра сделает его действительно достаточно безопасным.
«Это достаточно чисто, если исходить из предположения, что все значения Scheme являются шлифованными». Это не предположение. Если вы переводите процедуру Scheme в предикат Prolog, она должна правильно работать (как и Scheme) с наземными «входами», но может применяться и к неосновным (в противном случае зачем использовать (почти чистый) Prolog?) Фактическое предположение указано в первом абзаце.
«Вам также нужны логические значения схемы». Добавлено, спасибо. Хотя я думаю, что логические значения не важны. Другие Лиспы используют 'T и () для представления #t и #f.
может применяться к неназемным" Можно, но в данном случае вы не указали предполагаемую семантику. Изучение схемы с логическими переменными сделало бы этот вопрос слишком широким.
Я удалил свой собственный (=)/3 в пользу ссылки на полную реализацию.
Пока программа остается чистой и монотонной, ее можно использовать для объяснения, скажем, неожиданного сбоя. Это не байкшеринг! Ибо если погрузиться в нечистое, то останутся только процедурные интерпретации и их воплощение в трейсерах.
+100 за усилия, но я не согласен с вашей интерпретацией вопроса.
Вы могли бы заявить ранее, что не согласны с моей интерпретацией; вы все еще не говорите то, с чем не согласны. В примечании к награде особо упоминалось, что вы примете перевод вашего фрагмента схемы на Пролог. Я не реализовал транслятор, но дал переводы всех соответствующих конструкций Scheme.
Просто symbol/1 и dif/2 являются достаточными расширениями логически чистого Пролога.
Доказательство:
Этот ответ содержит оценщик выражений схемы, evil/2. Он понимает lambda и quote и может быть легко расширен для обработки встроенных процедур списка, таких как list, car, cdr и т. д. Помимо чистого (Горн) Пролога, он использует только symbol/1 и dif/2. Хотя это интерпретатор и будет работать медленно, его существование показывает, что те же самые операции со списками, которые выполняет Scheme, могут быть выполнены в таком почти чистом Прологе. (Я думаю, что symbol/1 тоже не понадобился бы, если бы символы Схемы были переведены в symb(prolog_atom), а не напрямую в prolog_symbol)
Редактировать
Это расширяет evil/2 для обработки if, #t и #f (представленных true и false):
evil(true, _, true).
evil(false, _, false).
evil([if, E1, E2, E3], Env, Val) :-
evil(E1, Env, false),
symbols(E2),
evil(E3, Env, Val).
evil([if, E1, E2, E3], Env, Val) :-
evil(E1, Env, V),
dif (V, false),
symbols(E3),
evil(E2, Env, Val).
Это расширяет evil/2 до обработки equalp. Он даже мощнее, чем Scheme eq*, поскольку он также будет приравнивать некоторые замыкания:
evil([equalp, E1, E2], Env, true) :-
evil(E1, Env, V),
evil(E2, Env, V).
evil([equalp, E1, E2], Env, false) :-
evil(E1, Env, V1),
evil(E2, Env, V2),
dif (V1, V2).
Это оценщик для выражений Scheme, который не включает в себя какую-либо форму cond или проверку на равенство. Учитывая, что проверка на равенство является основной проблемой для реализации пересечения списков, это «доказательство» не показывает того, что вы думаете.
@IsabelleNewbie Проблема с intersection заключалась в отсутствии неравенства. Однако этот Q специально позволяет dif/2. Я думаю, что evil/2 можно тривиально расширить для обработки условных операторов (используя (=)/2 и dif/2).
@IsabelleNewbie добавила if, #t и #f. (Хотя я помню некоторые дискуссии о том, что лямбда-исчисление не нуждается в них) - может быть глючит, т.к. что происходит, когда вы делаете (lambda (#t)..), но это должно быть поправимо, если это так.
«Хотя я помню некоторые дискуссии о том, что лямбда-исчисление не нуждается в них». Они не нужны в том смысле, что вы можете придумать способ кодирования их как лямбда-выражений, точно так же, как вы можете кодировать натуральные числа, списки, а все остальное как лямбды. Но если вы такой редукционист, ваш первоначальный вопрос становится бессмысленным: мы знаем, что вы можете закодировать схему на машине Тьюринга, поэтому неудивительно, что вы можете закодировать ее на Прологе.
В любом случае, здесь все еще не хватает equal?, для чего вам понадобится какой-то способ определения равенства, например, (=)/3.
@IsabelleNewbie «этому по-прежнему не хватает равенства?, для чего вам понадобится какой-то способ определить равенство, например, (=)/3». -- добавил equalp.
@IsabelleNewbie «Но если вы такой редукционист, ваш первоначальный вопрос становится бессмысленным: мы знаем, что вы можете кодировать схему на машине Тьюринга, поэтому неудивительно, что вы можете кодировать ее на Прологе» — я бы предложил вам повторить evil/3 без dif/2, используя только предложения Хорна, тогда (должны давать правильные и только правильные результаты)
@bobcat, почему вы описываете dif/2 как «расширение» логически чистого пролога. Отождествляем ли мы «логически чистый пролог» с «чистым прологом предложения Хорна»?
А как насчет (есть)/2? Вам в значительной степени нужны ошибки инстанцирования, nonvar/1 и Ground/1 для защиты от непреднамеренного использования.