Удаление ненужных элементов из списка в PROLOG - PullRequest
0 голосов
/ 11 мая 2018

У меня есть список, например [(1,2), (3,4), (5,2), (4,2), (8,0)], и я хочу удалить, например, все элементы, которые не являются (_, 2).Так что в этом случае я бы получил такой список: [(1,2), (5,2), (4,2)].Я пытался:

   conta_pos(NL, _, NL):-
      Idk what to do here, !.

   conta_pos([(L,C)|T], C, _):-
      conta_pos_aux([()], C, _).  

    conta_pos([(_,C)|T], _, _):-
      conta_pos(T, _, _).  

Первый аргумент представляет начальный список, второй - элемент, который я хочу сохранить в списке, а третий - мой новый список.
Имейте в виду, что яЯ новичок в Прологе, пожалуйста.

(Фактическое, что я хочу сделать, это подсчитать количество элементов в инициале, которые, в этом примере (_, 2), так что бы 3. Я думал о том, чтобы затем использовать длину \ 2чтобы посчитать их, но если у вас есть лучшее предложение, я полностью открыт для него! И если вы хотите знать, что я должен сделать в целом, не стесняйтесь спрашивать)

Ответы [ 4 ]

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

Вы описываете списки, так что вы можете использовать DCG для задачи, так как они обычно дают легко читаемый код.Кроме того, вы можете использовать дополнительный аргумент для подсчета элементов в списке по мере его обхода.Рассмотрим следующий код:

list_filtered_length(L,F,Len) :-    % the filtered list F is described
   phrase(filtered_len(L,Len,0),F). % by the DCG filtered_len//3 

filtered_len([],N,N) -->            % if the list is empty, the counter is the length
   [].                              % and the filtered list is empty
filtered_len([(A,2)|Ps],N,C0) -->   % if the head of the list is (A,2)
   {C1 is C0+1},                    % the counter is increased
   [(A,2)],                         % (A,2) is in the filtered list
   filtered_len(Ps,N,C1).           % the same for the tail
filtered_len([(_,B)|Ps],N,C) -->    % if the head of the list is (_,B)
   {dif(B,2)},                      % with B not being 2, it's not in the list
   filtered_len(Ps,N,C).            % the same for the tail

Запрос этого предиката в вашем примере дает желаемый результат:

?- list_filtered_length([(1,2),(3,4),(5,2),(4,2),(8,0)],F,Len).
F = [ (1, 2), (5, 2), (4, 2)],
Len = 3 ;
false.

Очевидно, что если вы хотите применить другой фильтр, вы должны переписатьдва рекурсивных правила DCG.Было бы лучше иметь фильтр, определенный как отдельный предикат, и передавать его в качестве аргумента, что делает предикат более универсальным.Также было бы неплохо, чтобы предикат успешно выполнялся, если существует только одно решение.Это можно реализовать с помощью if_ / 3 и (=) / 3 .Чтобы использовать его в качестве первого аргумента if_/3, предикату фильтра необходимо подтвердить значение истинности в качестве дополнительного аргумента:

filter_t((_,X),T) :-
   if_(X=2,T=true,T=false).

Как видите, последний аргумент - true, еслиусловие фильтра выполняется и false в противном случае:

?- filter_t((1,1),T).
T = false.

?- filter_t((1,2),T).
T = true.

Теперь предикат можно переопределить с дополнительным аргументом для фильтра, например:

list_filtered_by_length(L,LF,F_2,Len) :-    % F_2 is the filter argument
   phrase(filtered_by_len(L,F_2,Len,0),LF).

filtered_by_len([],_F_2,N,N) -->
   [].
filtered_by_len([P|Ps],F_2,N,C0) -->
   {if_(call(F_2,P),(X=[P], C1 is C0+1),
                    (X=[], C1 = C0))},
   X,                                       % X is in the filtered list
   filtered_by_len(Ps,F_2,N,C1).

Если заголовок спискаудовлетворяет условию фильтра (call(F_2,P)), он находится в фильтрованном списке (X=[P]) и счетчик увеличивается (C1 is C0+1), в противном случае его нет в списке (X=[]) и счетчик не увеличивается(C1 = C0).

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

?- list_filtered_by_length([(1,2),(3,4),(5,2),(4,2),(8,0)],F,filter_t,Len).
F = [ (1, 2), (5, 2), (4, 2)],
Len = 3.

Если вы хотите фильтровать что-то еще, просто определите другой предикат фильтра.Например, если вы хотите отфильтровать все пары одинаковых элементов из списка пар, вы можете определить ...

filter2_t(X-Y,T) :-
   if_(X=Y,T=true,T=false).

... и затем запрос:

?- list_filtered_by_length([a-a,b-c,d-d,e-f],F,filter2_t,Len).
F = [a-a, d-d],
Len = 2.

РЕДАКТИРОВАТЬ

В качестве альтернативы, вы можете выразить это отношение довольно компактно, используя tfilter / 3 , как предложено @false в комментариях.Как и в версии DCG, вы передаете предикат фильтра reifying в качестве третьего аргумента, который затем используется в качестве первого аргумента tfilter/3.Впоследствии длина отфильтрованного списка описывается встроенным length/2.

list_filtered_by_length(L,FL,F_2,Len) :-
   tfilter(F_2,L,FL),
   length(FL,Len).

. Вышеуказанные запросы дают те же ответы, что и в версии DCG.

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

Фильтрация элементов, сопоставляющих (посредством унификации) с шаблоном, у нас есть два рекурсивных правила (одно для случая, когда элемент соответствует Z, и одно для случая, когда элемент не совпадает):

filter([X|Xs],Z,[X|Ys]) :- % X is part of the output
    copy_term(Z,E),        % copy free variables (e.g. the _ in (_,2))
    E=X,                   % matches with template Z
    filter(Xs,Z,Ys).       % recurse

filter([X|Xs],Z,Ys) :-     % X is not part of the output
    copy_term(Z,E),        % copy free variables
    E\=X,                  % does NOT match with template Z
    filter(Xs,Z,Ys).       % recurse

filter([],_,[]).           % base step

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

?- filter([(1, 2), (3, 4), (5, 2), (4, 2), (8, 0)], (_, 2), Y), length(Y, Count).
Y = [(1, 2),  (5, 2),  (4, 2)],
Count = 3

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

count([X|Xs],Z,Count) :-
    copy_term(Z,E),
    E=X,
    count(Xs,Z,Count1),
    Count is Count1 + 1.

count([X|Xs],Z,Count) :-
    copy_term(Z,E),
    E\=X,
    count(Xs,Z,Count).

count([],_,0).

Тест:

?- count([(1,2), (3,4), (5,2), (4,2), (8,0)], (_,2), Count).
Count = 3

Два рекурсивных правила можно объединить в одно с условным оператором (->):

count([X|Xs],Z,Count) :-
    copy_term(Z,E),
    count(Xs,Z,Count1),
    ( E=X -> Count is Count1 + 1 ; Count=Count1 ).

count([],_,0).
0 голосов
/ 11 мая 2018

В общем, предположим, что у вас есть предикат some_condition(...other_args..., X), который успешно выполняется, если вы хотите сохранить X, и в противном случае не работает. Простая рекурсия для создания фильтра:

filter([], _, []).
filter([X|Xs], Condition, Ys) :-
    (    call(Condition, X)
    ->   Ys = [X|Zs]
    ;    Ys = Zs
    ),
    filter(Xs, Condition, Zs).

Затем вы можете назвать это как:

filter(RawList, some_condition(...other args...), FilteredList).

В этом конкретном случае вы хотите проверить, соответствует ли аргумент (_,2). Упрощенное условие будет:

second_arg_2((_,2)).  % Succeeds if second argument is a 2

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

Тогда вы можете позвонить:

| ?- filter([(1,2), (3,4), (5,2), (4,2), (8,0)], second_arg_2, R).

R = [(1,2),(5,2),(4,2)]

yes

Вы можете, конечно, подсчитать результаты, если хотите использовать length/2.

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

Для подсчета (_, 2) вы можете сделать

conta_pos(In, (_,V), Out) :-
    aggregate(count, X^member((X,V), In), Out).

Результат:

?- conta_pos( [(1,2), (3,4), (5,2), (4,2), (8,0)], (_,2), Out).
Out = 3.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...