Удаление всех вхождений элемента из списка

пытаясь написать процедуру, которая задала значение и список, он удаляет все появление этого значения в списке a написал:

delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).

С cut код не может ответьте правильно на такие вопросы, как:

delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2, 1, 2 ]).

если я удалю разрезы:

delMember(X, [], []).
delMember(X, [X|Xs], Y) :- delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).

он терпит неудачу в запросах, таких как:

delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).

(возвращает true , когда правильный ответ false).

как я могу сделать это работает в обе ситуации?

может быть, я могу проверить, что X is not T в третьей строке кода, я пробовал:

delMember(X, [T|Xs], Y) :- not(X = T), delMember(X, Xs, Y2), append([T], Y2, Y).

но это не работает.

4 ответов


использование разреза

delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).

здесь, вы можете видеть, что вы используете !/0 в последнем предложении вашего предиката. В этом нет необходимости. После последнего предложения выбора нет (Prolog запоминает точки выбора слева направо и сверху вниз), поэтому вырезать (который удаляет выбор), не будет делать ничего полезного, так как вы уже находитесь в нижней части списка вариантов.

для иллюстрации, см.

a :- b; c.
a :- d.

здесь, чтобы доказать a, Пролог сначала попробуй!--7-->, потом c, потом d (слева направо, сверху вниз).

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

Пролог рекурсия

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

Ваш третий пункт не так:

delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).

следует читать:

delMember(X, [T|Xs], [T|Y]) :- delMember(X, Xs, Y).

Ну, это не совсем неправильно, это просто очень неоптимально. Это не хвост-рекурсивный и использует append/3, что превратит ваш линейный предикат в квадратичный. Кроме того, как вы заметили, поскольку это не хвост-рекурсивно, прекращение сложнее получить в некоторых случаях.

затем, чтобы удалить использование разрез !/0, вы можете рассмотреть вопрос о добавлении гвардии до последнего пункта:

delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
    delMember(X, Xs, Y).
delMember(X, [T|Xs], [T|Y]) :-
    dif(X, T),
    delMember(X, Xs, Y).

гвардии, dif(X, T), указывает, что если мы находимся в третьем случае, мы не можем быть во втором одновременно:X невозможно объединить с T здесь.

Примечание, есть еще один способ, которым мы не можем использовать предикат, это,+, -, +, as cTI говорит нам. Поэтому запросы типа ?- delMember(1, R, [2, 3]). будет петля с моей версией.

Я надеюсь, что это было полезный.


это не истинный ответ, просто расширенное примечание к МНГ и thanosQR ответы, слишком долго, чтобы вписаться в комментарий. Такие ответы приятны и поучительны, но удаление разреза необходимо переосмыслить. Подумайте:

delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
    delMember(X, Xs, Y), !.
delMember(X, [T|Xs], [T|Y]) :-
    delMember(X, Xs, Y).

данное определение позволяет

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.

это не удается в исходном коде Mog ' из-за охранника в последней причине. Стоит отметить, что замена там охранника на X \== T (это ограничивает тест на соответствие instantiation state), также решить этот запрос, как отмечено thanosQR.

но ни один из этих фрагментов не решает общий случай:

?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;
Y = [1, 2, 1].

давайте немного перефразируем: так как вы хотите использовать в более чем одном шаблоне создания экземпляра "процедура, которая задала значение и список, удаляет все вхождение этого значения в списке", не определяет, как она должна вести себя в других случаях. Итак, мы, вероятно, хотим что-то вроде " предикат истинен, если второй и третий аргумент-это списки L1, L2 и L1-тот же список с L2, если мы игнорируем все вхождения первого аргумент"

теперь есть два способа написания предиката с несколькими возможными экземплярами; вы можете использовать мета-логические предикаты, такие как var/1 и ground/1 и написать код для каждого из них (что, вероятно, позволит вам написать код, оптимизированный для этого конкретного экземпляра) или написать код, который будет определять свойства логически (что может быть более сложным).

в этом случае, мы могли бы сделать что-то вроде этого:

    del(_, [], []).
    del(X, [X|L1], L2):-
        del(X,L1,L2).
    del(X, [H|L1], [H|L2]):-
        X\==H,
        del(X,L1,L2).

что имеет следующие характеристики:

19 ?- del(1, [1,2,3], X).
X = [2, 3] ;
false.
1,2,3,
20 ?- del(1, [1,2,3], [2,3]).
true ;
false.

21 ?- del(X, [1,2,3], [2,3]).
X = 1 ;
false.

22 ?- del(X, [1,2,3], Y).
X = 1,
Y = [2, 3] ;
X = 2,
Y = [1, 3] ;
X = 3,
Y = [1, 2] ;
Y = [1, 2, 3] ;
false.

23 ?- del(X, P, Y).
P = Y, Y = [] ;
P = [X],
Y = [] ;
P = [X, X],
Y = [] ;
P = [X, X, X],
Y = [] ;
P = [X, X, X, X],
Y = [] ;
P = [X, X, X, X, X],
Y = [] ;
P = [X, X, X, X, X, X],
Y = [] .

относительно последнего вызова; prolog возвращает список X, который растет, потому что используется алгоритм глубины; с помощью length/2 мы можем получить результаты, в ширину (_G означает, что переменная не создается (это может быть что угодно)):

24 ?- length(P,N), del(X, P, Y).
P = [],
N = 0,
Y = [] ;
P = [X],
N = 1,
Y = [] ;
P = [_G548],
N = 1,
Y = [_G548] ;
P = [X, X],
N = 2,
Y = [] ;
P = [X, _G551],
N = 2,
Y = [_G551] ;
P = [_G548, X],
N = 2,
Y = [_G548] ;
P = [_G548, _G551],
N = 2,
Y = [_G548, _G551] ;
P = [X, X, X],

edit: как указал @chac, предикат выше ведет себя неправильно, если первый список имеет (по крайней мере) один дубликат элемента:

?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;                  <----- wrong
Y = [1, 2, 1].

это так \==/2 и \=/2 фактически не накладывайте ограничение на переменную. это можно решить, переключив порядок правил в третьем предложении:

    del(_, [], []).
    del(X, [X|L1], L2):-
        del(X,L1,L2).
    del(X, [H|L1], [H|L2]):-
        del(X,L1,L2),
        X\==H.


4 ?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
Y = [1, 2, 1] ;
false.

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

del(X,L1,L2):-
    del(X,L1,L2,[]).

del(X, [], [], NotX):-
    \+ member(X,NotX).
del(X, [X|L1], L2, NotX):-
    del(X,L1,L2,NotX).
del(X, [H|L1], [H|L2], NotX):-
    X\==H,     % <--- optional; stops the execution earlier (saving time)
    del(X,L1,L2,[H|NotX]).

однако, согласно следующему, хвостовая рекурсивная версия на самом деле медленнее:

?-time(forall((between(1,50,N),length(X,N),del2(1,X,[2,3,2,3])),true)).
% 25,600,793 inferences, 5.468 CPU in 5.548 seconds (99% CPU, 4682134 Lips)
true.

?- time(forall((between(1,50,N),length(X,N),del_tr(1,X,[2,3,2,3])),true)).
% 37,346,143 inferences, 6.426 CPU in 6.428 seconds (100% CPU, 5811563 Lips)
true.

тем не менее, + - + не работает (он падает в бесконечный цикл.) но почему? проблема заключается в порядке п.: del (1, L1, [2]) сначала применит правило, которое "добавляет" X к главе L1, а затем применяет то же правило навсегда. этому можно противопоставить использование (снова) length/2:

?- length(X,2), del(1,X,[2]).
X = [1, 2] ;
X = [2, 1] ;
false.

в качестве альтернативы мы можем изменить порядок предложений:

del(_, [], []).
del(X, [H|L1], [H|L2]):-
    X\==H,
    del(X,L1,L2),
    X\==H.
del(X, [X|L1], L2):-
    del(X,L1,L2).

пока length/2 может быть полезно снова, так как без него prolog делает первый поиск глубины:

?- del(1,X,[2]).
X = [2] ;
X = [2, 1] ;
X = [2, 1, 1] ;
X = [2, 1, 1, 1] ;
X = [2, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1, 1] 

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


здесь идет фрагмент, который также работает с первым и третьим удаленными аргументами:

delMember(X, Y, Z):-
  bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).

я объясню здесь, что делает этот код. Идея в том, чтобы:

  1. Создайте список отдельных членов входного списка Y, которые объединяются с входным членом X
  2. затем для каждого X из списка, построенного на 1) отбросьте этот элемент из входного списка, чтобы получить выходной список Z без члена X.

Шаг 1 сделан с setof(X, member(X, Y), L) и это работает двумя способами. Когда параметр X уже создан экземпляр тогда L будет либо в список [X] если X содержится во входном параметре Y, из этого не выйдет, если X не содержит Y. С другой стороны, если X был подтвержден, то L будет набор отдельных элементов входного параметра Y.

теперь в шаге 2 мы возвращаемся к каждому элементу L, и для каждого члена этого списка мы фильтруем этот элемент из входного списка Y что дает результат. Мы собираем все эти элементы в выходном списке Z.

обратите внимание, что когда параметр X был удален при вызове процедуры wass, при обратном отслеживании member(X, Y) мы получим каждый член списка ввода Y, который будет использоваться для фильтрации.

тестовые случаи:

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
false.

?- delMember(Y, [1,2,3,1,2,3], X).
Y = 1,
X = [2, 3, 2, 3] ;
Y = 2,
X = [1, 3, 1, 3] ;
Y = 3,
X = [1, 2, 1, 2].

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.

?- delMember(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1].