Удаление заданного значения из списка и сохранение в аккумуляторе - PullRequest
0 голосов
/ 21 мая 2018

Пока у меня есть:

remove(_,[],R,R).

remove(X,[H|T],[],R) :- X =\= H, remove(X,T,[_|H],R).

Но я не могу понять, почему это не работает.Первый - это базовый случай, когда список исчерпан, и аккумулятор копируется в R.

Второй определяет, что голова не должна равняться X, а затем возвращается в хвост и добавляет Голову каккумулятор.

1 Ответ

0 голосов
/ 21 мая 2018

Прежде всего, =\= - это неравенство значений арифметических выражений.Это не общий «неравный» предикат.Например, это не очень хорошо работает:

?- a =\= b.
ERROR: =\=/2: Arithmetic: `a/0' is not a function

Таким образом, у вашего предиката нет надежды работать на что-либо, кроме списка чисел или других арифметических выражений.Если ваша система Prolog имеет dif/2, используйте это:

?- dif(a, b).
true.

Только если вышеприведенное не работает, используйте \= для неравенства:

?- a \= b.
true.

(разница междуэти два факта в том, что dif правильно обрабатывает переменные, а \= нет.)

Теперь одна непосредственная проблема в вашем коде состоит в том, что вы сопоставляете список со списком как [H|T], а затем пытаетесь создатьновый список [_|H] для рекурсивного вызова.Это поднимает красный флаг: если H является заголовком первого списка, то в общем случае это не будет сам список.Но не-списки не должны отображаться как tail в конструкторе списка.Например, если мы сопоставим список [1, 2, 3]:

?- [H|T] = [1, 2, 3].
H = 1,
T = [2, 3].

... и затем попытаемся создать новый список с этим H в качестве хвоста:

?- [H|T] = [1, 2, 3], Xs = [42 | H].
H = 1,
T = [2, 3],
Xs = [42|1].

... результат не будет списком:

?- [H|T] = [1, 2, 3], Xs = [42 | H], is_list(Xs).
false.

Вы, вероятно, хотите иметь H в качестве головы нового аккумулятора, например:

remove(X, [H|T], R0, R) :-
    dif(X, H),
    remove(X, T, [H|R0], R).

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

. В некоторых случаях это уже работает:

?- remove(a, [b, c, d], [], Result).
Result = [d, c, b] ;
false.

Но недля других:

?- remove(a, [a], [], Result).
false.

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

remove(X, [X|T], R0, R) :-
   ... %  fill in here

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

Однако, аккумулятор здесь не нужен.Результат может быть построен напрямую.Вот реализация:

remove(_X, [], []).
remove(X, [Y|Xs], [Y|Ys]) :-
    dif(X, Y),
    remove(X, Xs, Ys).
remove(X, [X|Xs], Ys) :-
    remove(X, Xs, Ys).

Это работает как для положительных, так и для отрицательных тестовых случаев:

?- remove(a, [b, a, c], Result).
Result = [b, c] ;
false.

?- remove(a, [b, c, d], Result).
Result = [b, c, d] ;
false.

Это работает в общем случае:

?- remove(X, Xs, Ys).
Xs = Ys, Ys = [] ;
Xs = Ys, Ys = [_G4794505],
dif(X, _G4794505) ;
Xs = Ys, Ys = [_G4794601, _G4794604],
dif(X, _G4794604),
dif(X, _G4794601) ;
Xs = Ys, Ys = [_G4794697, _G4794700, _G4794703],
dif(X, _G4794703),
dif(X, _G4794700),
dif(X, _G4794697) ;
Xs = Ys, Ys = [_G4794793, _G4794796, _G4794799, _G4794802],
dif(X, _G4794802),
dif(X, _G4794799),
dif(X, _G4794796),
dif(X, _G4794793) ;
Xs = Ys, Ys = [_G4794889, _G4794892, _G4794895, _G4794898, _G4794901],
dif(X, _G4794901),
dif(X, _G4794898),
dif(X, _G4794895),
dif(X, _G4794892),
dif(X, _G4794889) ;
Xs = Ys, Ys = [_G4794985, _G4794988, _G4794991, _G4794994, _G4794997, _G4795000],
dif(X, _G4795000),
dif(X, _G4794997),
dif(X, _G4794994),
dif(X, _G4794991),
dif(X, _G4794988),
dif(X, _G4794985) .

Ноперечисленное выше не справедливо!Это только упражнение второе, а не третье.Мы можем получить справедливое перечисление, сгруппировав по длине Xs:

?- length(Xs, N), remove(X, Xs, Ys).
Xs = Ys, Ys = [],
N = 0 ;
Xs = Ys, Ys = [_G4794582],
N = 1,
dif(X, _G4794582) ;
Xs = [X],
N = 1,
Ys = [] ;
Xs = Ys, Ys = [_G4794678, _G4794681],
N = 2,
dif(X, _G4794678),
dif(X, _G4794681) ;
Xs = [_G4794585, X],
N = 2,
Ys = [_G4794585],
dif(X, _G4794585) ;
Xs = [X, _G4794588],
N = 2,
Ys = [_G4794588],
dif(X, _G4794588) ;
Xs = [X, X],
N = 2,
Ys = [] ;
Xs = Ys, Ys = [_G4794774, _G4794777, _G4794780],
N = 3,
dif(X, _G4794774),
dif(X, _G4794780),
dif(X, _G4794777) .

Мы также можем использовать это «назад» ограниченным образом: оставив второй аргумент Xs свободным и указав конкретныйсписок для третьего аргумента Ys, мы получаем предикат , вставляющий данный элемент в Ys во всех возможных местах, давая Xs:

?- remove(a, Xs, [b, c, d]).
Xs = [b, c, d] ;
Xs = [b, c, d, a] ;
Xs = [b, c, d, a, a] ;
Xs = [b, c, d, a, a, a] .

Опять этонесправедливо, но опять же это можно исправить:

?- length(Xs, N), remove(a, Xs, [b, c, d]).
Xs = [b, c, d],
N = 3 ;
Xs = [b, c, d, a],
N = 4 ;
Xs = [b, c, a, d],
N = 4 ;
Xs = [b, a, c, d],
N = 4 ;
Xs = [a, b, c, d],
N = 4 ;
Xs = [b, c, d, a, a],
N = 5 .  % etc.
...