Выразителен ли почти чистый Пролог?

@ false прокомментировал ранее:

Да, вы можете реализовать машину Тьюринга без dif/2. Но вы даже не можете реализовать пересечение или аналогичные предикаты.

Предположим, мы расширяем чистый Пролог (Horn FOL + CWA + UNA) с call/N, dif/2 и (=)/3, для использования в if_/3, останутся ли пробелы в его выразительности, т.е. вещи, которые тривиально определить, скажем, в Scheme, но что гораздо сложнее сформулировать в таком расширенном (почти чистом) Prolog?

В частности, позволяет ли такой Prolog манипулировать списками Prolog так же удобно, как Scheme позволяет манипулировать списками Scheme?


Изменить: Предположить схему без мутаций, макросов, продолжений, лени, потоков, чисел, строк, векторов или символов. Просто символы, логические значения и списки (деревья).


person bobcat    schedule 23.12.2020    source источник
comment
А как насчет (is) / 2? Вам в значительной степени нужны ошибки создания экземпляров, nonvar / 1 и ground / 1 для защиты от непреднамеренного использования.   -  person false    schedule 23.12.2020
comment
@false Я думал о схеме без чисел, строк и символов. Просто символы. Отредактирую, чтобы уточнить.   -  person bobcat    schedule 23.12.2020


Ответы (3)


Просто символы и списки (деревья).

Вам также потребуются логические значения схемы #t и #f, если вы не хотите кодировать все в чистом лямбда-исчислении. Вы также исключаете значения функций, что, к счастью, упрощает этот ответ. Хотя вам придется разрешить особый случай (define name (lambda ...)) форм верхнего уровня. (Все остальное, включая расширенные let выражения, можно отключить.)

Итак, я утверждаю: Нет, нет разрыва в выразительности между этим расплывчатым подмножеством схемы и чистым Прологом, как вы его определили. Мой аргумент (это не доказательство) является конструктивным, поскольку я переведу код схемы для пересечения списков из этого ответа в Prolog.

В частности, это:

(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).

Обратите внимание, что вложенные выражения не должны быть вложенными, и во всех случаях, кроме самых тривиальных, это проще всего сделать, задав вспомогательные предикаты Пролога. Это не увеличивает размер кода нелинейно.

В этих определениях используются следующие переводы стандартных конструкций схемы:

'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 case и для else case:

result(Result, Result).

else('#t').

И это вся необходимая вам внутренне нечистая и внешне чистая поддержка Prolog:

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.
person Isabelle Newbie    schedule 27.12.2020
comment
Это (=)/3 не чисто. - person false; 27.12.2020
comment
Это достаточно чисто в предположении поста, что все значения схемы являются заземленными. Я хотел добавить это в ответ, обновлю. В любом случае замена моего (=)/3 вашим полным определением из stackoverflow.com/a/27358600/4391743 заставляет программу вести себя как такой же. - person Isabelle Newbie; 27.12.2020
comment
Нет необходимости в diff / 2. Вместо этого ошибка создания экземпляра сделает его действительно достаточно безопасным. - person false; 28.12.2020
comment
Это достаточно чисто в предположении поста, что все значения схемы являются обоснованными. Это не предположение. Если вы переводите процедуру Scheme в предикат Prolog, она должна работать правильно (как Scheme) с наземными входами, но может применяться к неосновным (в противном случае зачем использовать (почти чистый) Prolog?) Фактическое предположение указано в первом абзаце. - person bobcat; 28.12.2020
comment
Вам также нужны логические значения Scheme Добавлено, спасибо. Хотя я думаю, что логические значения не важны. Другие Лиспы используют 'T и () для обозначения #t и #f. - person bobcat; 28.12.2020
comment
это может быть применено к неосновным Может, но вы не указали предполагаемую семантику в этом случае. Изучение схемы с логическими переменными сделало бы этот вопрос слишком широким. - person Isabelle Newbie; 28.12.2020
comment
Я удалил свой (=)/3 в пользу ссылки на полную реализацию. - person Isabelle Newbie; 28.12.2020
comment
Пока программа остается чистой и монотонной, ее можно использовать для объяснения, например, неожиданного сбоя. Это не велосипедная распродажа! Ибо, если вы погрузитесь в чистоту, все, что останется, - это чисто процедурные интерпретации и их воплощение в трассерах. - person false; 29.12.2020
comment
+100 за усилие, но я не согласен с вашей интерпретацией Q. - person bobcat; 01.01.2021
comment
Вы могли раньше заявить, что не согласны с моей интерпретацией; вы все еще не говорите то, с чем не согласны. В уведомлении о награде конкретно упоминается, что вы примете перевод фрагмента вашей схемы на Prolog. Я не реализовал переводчик, но предоставил переводы всех соответствующих конструкций схемы. - person Isabelle Newbie; 01.01.2021

Если взять чистую схему. Возможно, достаточно чистого Пролога. Чистая схема - это лямбда-выражения с определенной строгой стратегией оценки по значению. Таким образом, мы могли бы реализовать чистую схему в Прологе следующим образом, используя индексы deBruijn:

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)
person Mostowski Collapse    schedule 23.12.2020

Просто symbol/1 и dif/2 являются достаточными расширениями логически чистого Prolog.

Доказательство:

Этот ответ содержит средство оценки для выражений схемы, evil/2. Он понимает lambda и quote и может быть легко расширен для обработки встроенных процедур списков, таких как list, car, cdr и т. Д. Помимо чистого (Horn) Prolog, он использует только 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).
person bobcat    schedule 31.12.2020
comment
Это средство оценки для выражений схемы, которые не включают в себя какие-либо формы cond или проверки на равенство. Учитывая, что проверка на равенство является основной проблемой для реализации пересечения списков, это доказательство не показывает того, что, по вашему мнению, оно делает. - person Isabelle Newbie; 31.12.2020
comment
@IsabelleNewbie Проблема с intersection заключалась в отсутствии неравенства. Однако этот Q специально допускает dif/2. Я думаю, что evil/2 можно тривиально расширить для обработки условных операторов (используя (=)/2 и dif/2). - person bobcat; 31.12.2020
comment
@IsabelleNewbie добавила if, #t и #f. (Хотя я помню некоторые дискуссии о том, что лямбда-исчислению они не нужны) - в w.r.t. может быть ошибка. что происходит, когда вы делаете (lambda (#t)..), но если это так, это можно исправить. - person bobcat; 31.12.2020
comment
Хотя я помню некоторые дискуссии о том, что лямбда-исчислению они не нужны. Они не нужны в том смысле, что вы можете придумать способ кодирования их как лямбда точно так же, как вы можете кодируйте натуральные числа, списки и все остальное как лямбды. Но если вы такой редукционист, ваш исходный вопрос станет бессмысленным: мы знаем, что вы можете кодировать схему на машине Тьюринга, поэтому неудивительно, что вы можете кодировать ее на Prolog. - person Isabelle Newbie; 31.12.2020
comment
В любом случае, здесь все еще отсутствует equal?, для чего вам понадобится какой-то способ определения равенства, например, (=)/3. - person Isabelle Newbie; 31.12.2020
comment
@IsabelleNewbie здесь все еще не хватает равенства?, Для чего вам понадобится какой-то способ определения равенства, например (=) / 3. - добавлено equalp. - person bobcat; 01.01.2021
comment
@IsabelleNewbie Но если вы такой редукционист, ваш исходный вопрос становится бессмысленным: мы знаем, что вы можете кодировать схему на машине Тьюринга, поэтому неудивительно, что вы можете кодировать ее на Prolog - I ' d попросить вас воспроизвести evil/3 без dif/2, используя только предложения Horn, затем (должны давать правильные и только правильные результаты) - person bobcat; 01.01.2021
comment
@bobcat, почему вы описываете dif/2 как расширение логически чистого пролога. Отождествляем ли мы логически чистый пролог с чистым прологом предложения Хорна? - person Jason Hemann; 05.03.2021