Удаление всех вхождений элемента из списка
пытаясь написать процедуру, которая задала значение и список, он удаляет все появление этого значения в списке 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).
я объясню здесь, что делает этот код. Идея в том, чтобы:
- Создайте список отдельных членов входного списка Y, которые объединяются с входным членом X
- затем для каждого 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].