从PROLOG中的列表中删除不需要的元素

时间:2018-05-11 00:08:27

标签: prolog

我有一个列表,例如[(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, _, _).  

第一个参数表示初始列表,第二个参数表示我希望列表保留的元素,第三个参数表示我的新列表。
请记住,我对Prolog很新。

(我想要做的实际情况是计算初始中的元素数量,在本例中为(_,2),这样就可以了3.我想的是然后使用length \ 2来计算它们,但如果你有更好的建议,我会全力以赴!如果你想知道我要做的是什么,请随时问一下)

1 个答案:

答案 0 :(得分:3)

您正在描述列表,因此您可以将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版本相同的答案。