Я пытаюсь понять, что утверждают и отказываются делать с термином, который они передают. Если я запускаю следующее:

?- assertz(item(first)).
true.

?- assertz(item(second)).
true.

?- assertz((item(Y) :- =(Y, third), writeln(Y))).
true.

?- retract(item(X)).
X = first ;
X = second ;
false.

Retract / 1 удаляет все элементы, но моя writeln / 1 никогда не вызывается, поэтому кажется, что retract на самом деле не разрешает термин, который ему передан. Похоже, что он делает какую-то специальную операцию, где это:

  • Унифицирует термин с только фактами (то есть правилами без хвоста) в базе данных
  • Отводит каждого после применения замены от объединения

Это правильно? Или здесь что-то еще происходит?

Сказано иначе: если я напишу свое собственное правило с одним аргументом, Prolog не объединит элемент (X) автоматически с базой данных и не выполнит итерацию по всем фактам, которые относятся к item / 1. Это просто дает мне предмет (X). Как ретракт делает свое волшебство?

?- assertz((myRule(X) :- writeln(X))).
true.

? myRule(item(X)).
item(_2556)
true.

Ответьте с дополнительными разъяснениями. Основываясь на ответе пользователя 9213, кажется, что ответ «втягивание (но не утверждение!) Имеет забавное поведение, когда он выполняет простую унификацию своего термина перед тем, как что-то сделать с базой данных». Мне остается удивляться, почему другие встроенные функции, которые я видел (например, atom / 1 и writeln / 1), по-видимому, этого не делают. Может быть, это чаще, чем я испытал?

Для согласованности, похоже, что для ретракта должны требоваться базовые термины, и если пользователь хочет сделать что-то подобное моему примеру выше, он мог бы сделать это:

? - (предмет (X), убрать (предмет (X))).

Все это заставляет меня думать, что я что-то упустил, или, может быть, эти встроенные функции были просто непоследовательны? Любое разъяснение, которое объясняет это, было бы здорово.

2
Eric Zinda 2 Май 2019 в 01:25

3 ответа

Лучший ответ

Чтобы добавить мой голос в припев, да, все эти предикаты (assert*, retract*) манипулируют базой данных Пролога фактов и правил; они не оценивают свои аргументы , как если бы они были фактами или правилами. Да, они просто «синтаксически» объединяют свои аргументы с фактами и правилами в базе данных.

Пролог - это гомоиконический язык. Программой можно манипулировать так, как если бы это были данные; структуры данных могут быть интерпретированы, как если бы они были программой. Самое главное: является ли определенная структура data или program , зависит только от контекста.

Вы, кажется, понимаете это, но для следующей бедной души, которая натыкается на ваш вопрос и этот ответ, вот игрушечный пример для демонстрации.

Существует предикат atom/1 это успешно, если его аргумент, ну, в общем, атом:

?- atom(foo).
true.

?- atom(Bar). % Bar is a free variable, which is not an atom
false.

?- atom(atom(bar)). % atom(bar) is a compound term, which is not an atom
false.

Но atom(bar) - это просто составной термин в последнем запросе. Это сложный термин с функторным атомом / 1. Единственный аргумент - это атом. Так что если вы сейчас запросили это:

?- atom(bar).
true.

Есть также этот действительно хороший предикат , называемый call . Это облегчает стирание граней между программой и данными. Следующие три запроса идентичны по значению:

?- atom(bar).
true.

?- call(atom, bar).
true.

?- call(atom(bar)).
true.

Кроме того, вы можете динамически (во время выполнения) создавать структуру данных и оценивать ее как программу. Вот наивная реализация call/2, которая может оценивать предикаты, предоставляя имя предиката и список аргументов. Он создает составной термин с использованием «univ» и оценивает его, просто помещая его в слот, где должна быть подцель. Я назову его my_call/2 и добавлю в базу данных, используя assertz:

?- assertz((my_call(Name, Args) :- Goal =.. [Name|Args], Goal)).
true.

?- my_call(between, [1, 3, X]).
X = 1 ;
X = 2 ;
X = 3.

Вы видите, что происходит? (Примечание: кажется, что call является относительно новым дополнением к Прологу. Прежде чем call был широко доступен, вы должны были сделать этот трюк, если вы хотели мета-вызов предикатов. Я не знаю этого исходя из своего опыта, просто образованное предположение, основанное на вещах, которые я прочитал или услышал, и не могу сейчас цитировать.)


Итак, давайте попробуем это снова. На самом деле все сложнее, но очень упрощенно:

Программа Пролог - это набор предикатов. Не заказано!

Каждый предикат представляет собой список предложений. Это список, потому что предикат может иметь 0 или более предложений, и они имеют порядок.

Предложение может быть фактом или правилом.

Правило - это составной термин с функтором :-/2. Да, это правильно. Чтобы понять, что я имею в виду:

?- assertz(:-(foo(X), between(1, 3, X))).
true.

?- listing(foo/1).
:- dynamic foo/1.

foo(A) :-
    between(1, 3, A).

true.

Это просто еще один способ написать это. Это более условно.

Итак, действительно, эти два очень разные:

foo(X). % a fact
foo(X) :- between(1, 3, X). % a rule

Вы не можете объединить одно с другим. Вот почему, если вы хотите убрать foo(X) :- between(1, 3, X), вы не можете просто передать foo(X) в качестве аргумента для отвода. Или, чтобы завершить ваш пример:

?- assertz(item(first)).
true.

?- assertz(item(second)).
true.

?- assertz((item(Y) :- =(Y, third), writeln(Y))).
true.

?- retract(item(X)).
X = first ;
X = second ;
false.

?- retract((item(X) :- X = Y, writeln(X))).
Y = third.

Вы видите это сейчас?

2
User9213 2 Май 2019 в 19:16

У меня было смутное воспоминание о существовании retractall/1, который живет рядом с retract/1, и получается, что причина в таких ситуациях. retract/1 принимает Prolog term , как указано в документации:

< Сильный > отводной ( + Term)

Когда термин является атомом или термином, он объединяется с первым объединяющим фактом или предложением в базе данных. Факт или предложение удален из базы данных.

Акцент добавлен мной. Это иллюстрируется следующим:

?- asserta(foo(X) :- X = 2 ; X = 4).
true.

?- foo(X).
X = 2 ;
X = 4.

?- retract(foo(X)).
false.

?- foo(X).
X = 2 ;
X = 4.

?- retract(foo(X) :- X = 2 ; X = 4).
true.

?- foo(X).
false.

Обратите внимание, что если вы предоставите полное предложение , которое вы дали asserta/1, оно будет отозвано. Как @CapelliC указывает ниже, вы также можете предоставить предложение с переменной в качестве параметра, чтобы убирать вещи с телами:

retract(foo(X) :- Y).

Однако, если вы делаете хотите отозвать вещи, которые соответствуют шаблону, вы можете использовать retractall/1, что указано в документации:

< Сильный > retractall ( + Head )

Все факты или пункты в базе данных, для которых глава объединяется с главой, удаляются.

Это иллюстрируется следующим:

?- asserta(foo(X) :- X = 2 ; X = 4).
true.

?- foo(X).
X = 2 ;
X = 4.

?- retractall(foo(X)).
true.

?- foo(X).
false.

Другое важное различие между этими двумя заключается в том, что retract/1 удаляет по одной вещи за раз и объединяет эти вещи:

?- asserta(foo(X) :- X = 2).
true.

?- asserta(foo(X) :- X = 4).
true.

?- retract(foo(X) :- Y).
Y =  (X=4) ;
Y =  (X=2) .

Это в отличие от retractall/1, который удалит все , которое соответствует шаблону, без объединения чего-либо:

?- asserta(foo(X) :- X=2).
true.

?- asserta(foo(X) :- X=4).
true.

?- foo(X).
X = 4 ;
X = 2.

?- retractall(foo(X)).
true.

?- foo(X).
false.

Таким образом, retract/1 действительно предназначен для выполнения откатов за один раз, но ожидает что-то в форме, подобной термину, который вы указали, тогда как retractall/1 хочет только голову, обрабатывает ее как шаблон и удаляет как много вещей, которые соответствуют шаблону.

Это, безусловно, помогло мне улучшить мое понимание Пролога, поэтому я надеюсь, что это поможет и вам!

2
Daniel Lyons 2 Май 2019 в 17:20

определение assert в swi-prolog является:

Утвердить предложение (факт или правило) в базе данных. Предикат asserta / 1 утверждает предложение как первое предложение предиката, тогда как assertz / 1 утверждает предложение как последнее предложение. Устаревший assert / 1 эквивалентен assertz / 1 . Если пространство программы для целевого модуля ограничено (см. Set_module / 1), asserta / 1 может вызвать исключение resource_error (program_space). Пример ниже добавляет два факта и правило. Обратите внимание на двойные скобки вокруг правила.

Следовательно, он ничего не называет и не делает вывод! Это просто утверждение факта и правила в активной памяти.

1
OmG 1 Май 2019 в 23:07