Вычитание списка SWI Prolog выдает ошибку: из локального стека

#swi-prolog

#пролог

Вопрос:

Я запускаю некоторое правило Prolog, которое использует функцию вычитания, и в трассировке стека я обнаружил, что источником ошибки является это:

 lists:subtract([b, d | _], [b, d] , [r]) ? creep
ERROR: Out of local stack
 

Исходный вызов был:

 member(b, X), member(d, X), subtract(X, [b, d], [r]).
 

и ожидаемый результат [b, d, r] .

Я новичок в Prolog и не могу понять источник ошибки и как ее исправить. Пожалуйста, помогите.

Ответ №1:

невозможно понять источник ошибки и как ее исправить.

Просто возьмите свой запрос и посмотрите только на первые две цели:

 ?- member(b, X), member(d, X).
   X = [b,d|_A]
;  X = [b,_A,d|_B]
;  X = [b,_A,_B,d|_C]
;  X = [b,_A,_B,_C,d|_D]
;  X = [b,_A,_B,_C,_D,d|_E]
;  ... .
 

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

Итак, прежде всего, вам нужно как-то это исправить.

Затем рассмотрим значение subtract/3 в SWI:

 ?- subtract([b,d,r], [b,d], [r]).
true.

?- subtract([b,d,X], [b,d], [r]).
false. % ?? why not X = r?
 

Из одного этого вы можете видеть, что subtract/3 это не отношение. Таким образом, вы не можете использовать его как отношение, например, append/3 .

Чтобы исправить это и сохранить как можно ближе к исходному запросу, используйте library(reif) и library(lambda) :

 ?- S1=[b,d,X], S2 = [b,d], tpartition(S2 E^memberd_t(E,S2),S1,_,[r]).
   S1 = [b,d,r], X = r, S2 = [b,d].
 

Ответ №2:

Из руководства SWI Prolog :

library(lists) Содержит ряд старых предикатов для управления наборами, представленными в виде неупорядоченных списков, в частности intersection/3 , union/3 , subset/2 и subtract/3 . Все эти предикаты используются memberchk/2 для поиска эквивалентных элементов. В результате это нелогично, а объединение может легко привести к сомнительным результатам.

У вас возникла эта проблема, потому что subtract не является чистым и требует создания первых двух аргументов, следовательно , знак в его документации.

 subtract( Set,  Delete, -Result)
 

вместо этого вы можете использовать union / 3

 union( Set1,  Set2, -Set3)
 

вы можете узнать больше о других индикаторах режима здесь.